linux/arch/mips/kernel/smp-mt.c
<<
>>
Prefs
   1/*
   2 *  This program is free software; you can distribute it and/or modify it
   3 *  under the terms of the GNU General Public License (Version 2) as
   4 *  published by the Free Software Foundation.
   5 *
   6 *  This program is distributed in the hope it will be useful, but WITHOUT
   7 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   8 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   9 *  for more details.
  10 *
  11 *  You should have received a copy of the GNU General Public License along
  12 *  with this program; if not, write to the Free Software Foundation, Inc.,
  13 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  14 *
  15 * Copyright (C) 2004, 05, 06 MIPS Technologies, Inc.
  16 *    Elizabeth Clarke (beth@mips.com)
  17 *    Ralf Baechle (ralf@linux-mips.org)
  18 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org)
  19 */
  20#include <linux/kernel.h>
  21#include <linux/sched.h>
  22#include <linux/cpumask.h>
  23#include <linux/interrupt.h>
  24#include <linux/irqchip/mips-gic.h>
  25#include <linux/compiler.h>
  26#include <linux/smp.h>
  27
  28#include <linux/atomic.h>
  29#include <asm/cacheflush.h>
  30#include <asm/cpu.h>
  31#include <asm/processor.h>
  32#include <asm/hardirq.h>
  33#include <asm/mmu_context.h>
  34#include <asm/time.h>
  35#include <asm/mipsregs.h>
  36#include <asm/mipsmtregs.h>
  37#include <asm/mips_mt.h>
  38
  39static void __init smvp_copy_vpe_config(void)
  40{
  41        write_vpe_c0_status(
  42                (read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0);
  43
  44        /* set config to be the same as vpe0, particularly kseg0 coherency alg */
  45        write_vpe_c0_config( read_c0_config());
  46
  47        /* make sure there are no software interrupts pending */
  48        write_vpe_c0_cause(0);
  49
  50        /* Propagate Config7 */
  51        write_vpe_c0_config7(read_c0_config7());
  52
  53        write_vpe_c0_count(read_c0_count());
  54}
  55
  56static unsigned int __init smvp_vpe_init(unsigned int tc, unsigned int mvpconf0,
  57        unsigned int ncpu)
  58{
  59        if (tc > ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT))
  60                return ncpu;
  61
  62        /* Deactivate all but VPE 0 */
  63        if (tc != 0) {
  64                unsigned long tmp = read_vpe_c0_vpeconf0();
  65
  66                tmp &= ~VPECONF0_VPA;
  67
  68                /* master VPE */
  69                tmp |= VPECONF0_MVP;
  70                write_vpe_c0_vpeconf0(tmp);
  71
  72                /* Record this as available CPU */
  73                set_cpu_possible(tc, true);
  74                set_cpu_present(tc, true);
  75                __cpu_number_map[tc]    = ++ncpu;
  76                __cpu_logical_map[ncpu] = tc;
  77        }
  78
  79        /* Disable multi-threading with TC's */
  80        write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
  81
  82        if (tc != 0)
  83                smvp_copy_vpe_config();
  84
  85        return ncpu;
  86}
  87
  88static void __init smvp_tc_init(unsigned int tc, unsigned int mvpconf0)
  89{
  90        unsigned long tmp;
  91
  92        if (!tc)
  93                return;
  94
  95        /* bind a TC to each VPE, May as well put all excess TC's
  96           on the last VPE */
  97        if (tc >= (((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1))
  98                write_tc_c0_tcbind(read_tc_c0_tcbind() | ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT));
  99        else {
 100                write_tc_c0_tcbind(read_tc_c0_tcbind() | tc);
 101
 102                /* and set XTC */
 103                write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (tc << VPECONF0_XTC_SHIFT));
 104        }
 105
 106        tmp = read_tc_c0_tcstatus();
 107
 108        /* mark not allocated and not dynamically allocatable */
 109        tmp &= ~(TCSTATUS_A | TCSTATUS_DA);
 110        tmp |= TCSTATUS_IXMT;           /* interrupt exempt */
 111        write_tc_c0_tcstatus(tmp);
 112
 113        write_tc_c0_tchalt(TCHALT_H);
 114}
 115
 116static void vsmp_send_ipi_single(int cpu, unsigned int action)
 117{
 118        int i;
 119        unsigned long flags;
 120        int vpflags;
 121
 122#ifdef CONFIG_MIPS_GIC
 123        if (gic_present) {
 124                gic_send_ipi_single(cpu, action);
 125                return;
 126        }
 127#endif
 128        local_irq_save(flags);
 129
 130        vpflags = dvpe();       /* can't access the other CPU's registers whilst MVPE enabled */
 131
 132        switch (action) {
 133        case SMP_CALL_FUNCTION:
 134                i = C_SW1;
 135                break;
 136
 137        case SMP_RESCHEDULE_YOURSELF:
 138        default:
 139                i = C_SW0;
 140                break;
 141        }
 142
 143        /* 1:1 mapping of vpe and tc... */
 144        settc(cpu);
 145        write_vpe_c0_cause(read_vpe_c0_cause() | i);
 146        evpe(vpflags);
 147
 148        local_irq_restore(flags);
 149}
 150
 151static void vsmp_send_ipi_mask(const struct cpumask *mask, unsigned int action)
 152{
 153        unsigned int i;
 154
 155        for_each_cpu(i, mask)
 156                vsmp_send_ipi_single(i, action);
 157}
 158
 159static void vsmp_init_secondary(void)
 160{
 161#ifdef CONFIG_MIPS_GIC
 162        /* This is Malta specific: IPI,performance and timer interrupts */
 163        if (gic_present)
 164                change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 |
 165                                         STATUSF_IP4 | STATUSF_IP5 |
 166                                         STATUSF_IP6 | STATUSF_IP7);
 167        else
 168#endif
 169                change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 |
 170                                         STATUSF_IP6 | STATUSF_IP7);
 171}
 172
 173static void vsmp_smp_finish(void)
 174{
 175        /* CDFIXME: remove this? */
 176        write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ));
 177
 178#ifdef CONFIG_MIPS_MT_FPAFF
 179        /* If we have an FPU, enroll ourselves in the FPU-full mask */
 180        if (cpu_has_fpu)
 181                cpumask_set_cpu(smp_processor_id(), &mt_fpu_cpumask);
 182#endif /* CONFIG_MIPS_MT_FPAFF */
 183
 184        local_irq_enable();
 185}
 186
 187/*
 188 * Setup the PC, SP, and GP of a secondary processor and start it
 189 * running!
 190 * smp_bootstrap is the place to resume from
 191 * __KSTK_TOS(idle) is apparently the stack pointer
 192 * (unsigned long)idle->thread_info the gp
 193 * assumes a 1:1 mapping of TC => VPE
 194 */
 195static void vsmp_boot_secondary(int cpu, struct task_struct *idle)
 196{
 197        struct thread_info *gp = task_thread_info(idle);
 198        dvpe();
 199        set_c0_mvpcontrol(MVPCONTROL_VPC);
 200
 201        settc(cpu);
 202
 203        /* restart */
 204        write_tc_c0_tcrestart((unsigned long)&smp_bootstrap);
 205
 206        /* enable the tc this vpe/cpu will be running */
 207        write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A);
 208
 209        write_tc_c0_tchalt(0);
 210
 211        /* enable the VPE */
 212        write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA);
 213
 214        /* stack pointer */
 215        write_tc_gpr_sp( __KSTK_TOS(idle));
 216
 217        /* global pointer */
 218        write_tc_gpr_gp((unsigned long)gp);
 219
 220        flush_icache_range((unsigned long)gp,
 221                           (unsigned long)(gp + sizeof(struct thread_info)));
 222
 223        /* finally out of configuration and into chaos */
 224        clear_c0_mvpcontrol(MVPCONTROL_VPC);
 225
 226        evpe(EVPE_ENABLE);
 227}
 228
 229/*
 230 * Common setup before any secondaries are started
 231 * Make sure all CPU's are in a sensible state before we boot any of the
 232 * secondaries
 233 */
 234static void __init vsmp_smp_setup(void)
 235{
 236        unsigned int mvpconf0, ntc, tc, ncpu = 0;
 237        unsigned int nvpe;
 238
 239#ifdef CONFIG_MIPS_MT_FPAFF
 240        /* If we have an FPU, enroll ourselves in the FPU-full mask */
 241        if (cpu_has_fpu)
 242                cpumask_set_cpu(0, &mt_fpu_cpumask);
 243#endif /* CONFIG_MIPS_MT_FPAFF */
 244        if (!cpu_has_mipsmt)
 245                return;
 246
 247        /* disable MT so we can configure */
 248        dvpe();
 249        dmt();
 250
 251        /* Put MVPE's into 'configuration state' */
 252        set_c0_mvpcontrol(MVPCONTROL_VPC);
 253
 254        mvpconf0 = read_c0_mvpconf0();
 255        ntc = (mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT;
 256
 257        nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1;
 258        smp_num_siblings = nvpe;
 259
 260        /* we'll always have more TC's than VPE's, so loop setting everything
 261           to a sensible state */
 262        for (tc = 0; tc <= ntc; tc++) {
 263                settc(tc);
 264
 265                smvp_tc_init(tc, mvpconf0);
 266                ncpu = smvp_vpe_init(tc, mvpconf0, ncpu);
 267        }
 268
 269        /* Release config state */
 270        clear_c0_mvpcontrol(MVPCONTROL_VPC);
 271
 272        /* We'll wait until starting the secondaries before starting MVPE */
 273
 274        printk(KERN_INFO "Detected %i available secondary CPU(s)\n", ncpu);
 275}
 276
 277static void __init vsmp_prepare_cpus(unsigned int max_cpus)
 278{
 279        mips_mt_set_cpuoptions();
 280}
 281
 282struct plat_smp_ops vsmp_smp_ops = {
 283        .send_ipi_single        = vsmp_send_ipi_single,
 284        .send_ipi_mask          = vsmp_send_ipi_mask,
 285        .init_secondary         = vsmp_init_secondary,
 286        .smp_finish             = vsmp_smp_finish,
 287        .boot_secondary         = vsmp_boot_secondary,
 288        .smp_setup              = vsmp_smp_setup,
 289        .prepare_cpus           = vsmp_prepare_cpus,
 290};
 291
 292#ifdef CONFIG_PROC_FS
 293static int proc_cpuinfo_chain_call(struct notifier_block *nfb,
 294        unsigned long action_unused, void *data)
 295{
 296        struct proc_cpuinfo_notifier_args *pcn = data;
 297        struct seq_file *m = pcn->m;
 298        unsigned long n = pcn->n;
 299
 300        if (!cpu_has_mipsmt)
 301                return NOTIFY_OK;
 302
 303        seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
 304
 305        return NOTIFY_OK;
 306}
 307
 308static int __init proc_cpuinfo_notifier_init(void)
 309{
 310        return proc_cpuinfo_notifier(proc_cpuinfo_chain_call, 0);
 311}
 312
 313subsys_initcall(proc_cpuinfo_notifier_init);
 314#endif
 315