1/* 2 * Copyright (C) 2007, 2011 MIPS Technologies, Inc. 3 * All rights reserved. 4 5 * This program is free software; you can distribute it and/or modify it 6 * under the terms of the GNU General Public License (Version 2) as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 17 * 18 * Arbitrary Monitor interface 19 */ 20 21#include <linux/kernel.h> 22#include <linux/init.h> 23#include <linux/smp.h> 24 25#include <asm/addrspace.h> 26#include <asm/mips-boards/launch.h> 27#include <asm/mipsmtregs.h> 28 29int amon_cpu_avail(int cpu) 30{ 31 struct cpulaunch *launch = (struct cpulaunch *)CKSEG0ADDR(CPULAUNCH); 32 33 if (cpu < 0 || cpu >= NCPULAUNCH) { 34 pr_debug("avail: cpu%d is out of range\n", cpu); 35 return 0; 36 } 37 38 launch += cpu; 39 if (!(launch->flags & LAUNCH_FREADY)) { 40 pr_debug("avail: cpu%d is not ready\n", cpu); 41 return 0; 42 } 43 if (launch->flags & (LAUNCH_FGO|LAUNCH_FGONE)) { 44 pr_debug("avail: too late.. cpu%d is already gone\n", cpu); 45 return 0; 46 } 47 48 return 1; 49} 50 51void amon_cpu_start(int cpu, 52 unsigned long pc, unsigned long sp, 53 unsigned long gp, unsigned long a0) 54{ 55 volatile struct cpulaunch *launch = 56 (struct cpulaunch *)CKSEG0ADDR(CPULAUNCH); 57 58 if (!amon_cpu_avail(cpu)) 59 return; 60 if (cpu == smp_processor_id()) { 61 pr_debug("launch: I am cpu%d!\n", cpu); 62 return; 63 } 64 launch += cpu; 65 66 pr_debug("launch: starting cpu%d\n", cpu); 67 68 launch->pc = pc; 69 launch->gp = gp; 70 launch->sp = sp; 71 launch->a0 = a0; 72 73 smp_wmb(); /* Target must see parameters before go */ 74 launch->flags |= LAUNCH_FGO; 75 smp_wmb(); /* Target must see go before we poll */ 76 while ((launch->flags & LAUNCH_FGONE) == 0) 77 ; 78 smp_rmb(); /* Target will be updating flags soon */ 79 pr_debug("launch: cpu%d gone!\n", cpu); 80} 81 82/* 83 * Restart the CPU ready for amon_cpu_start again 84 * A bit of a hack... it duplicates the CPU startup code that 85 * lives somewhere in the boot monitor. 86 */ 87void amon_cpu_dead(void) 88{ 89 struct cpulaunch *launch; 90 unsigned int r0; 91 int cpufreq; 92 93 launch = (struct cpulaunch *)CKSEG0ADDR(CPULAUNCH); 94 launch += smp_processor_id(); 95 96 launch->pc = 0; 97 launch->gp = 0; 98 launch->sp = 0; 99 launch->a0 = 0; 100 launch->flags = LAUNCH_FREADY; 101 102 cpufreq = 500000000; 103 104 __asm__ __volatile__ ( 105 " .set push\n\t" 106 " .set noreorder\n\t" 107 "1:\n\t" 108 " lw %[r0],%[LAUNCH_FLAGS](%[launch])\n\t" 109 " andi %[r0],%[FGO]\n\t" 110 " bnez %[r0],1f\n\t" 111 " nop\n\t" 112 " mfc0 %[r0],$9\n\t" /* Read Count */ 113 " addu %[r0],%[waitperiod]\n\t" 114 " mtc0 %[r0],$11\n\t" /* Write Compare */ 115 " wait\n\t" 116 " b 1b\n\t" 117 " nop\n\t" 118 "1: lw $ra,%[LAUNCH_PC](%[launch])\n\t" 119 " lw $gp,%[LAUNCH_GP](%[launch])\n\t" 120 " lw $sp,%[LAUNCH_SP](%[launch])\n\t" 121 " lw $a0,%[LAUNCH_A0](%[launch])\n\t" 122 " move $a1,$zero\n\t" 123 " move $a2,$zero\n\t" 124 " move $a3,$zero\n\t" 125 " lw %[r0],%[LAUNCH_FLAGS](%[launch])\n\t" 126 " ori %[r0],%[FGONE]\n\t" 127 " jr $ra\n\t" 128 " sw %[r0],%[LAUNCH_FLAGS](%[launch])\n\t" 129 " .set pop\n\t" 130 : [r0] "=&r" (r0) 131 : [launch] "r" (launch), 132 [FGONE] "i" (LAUNCH_FGONE), 133 [FGO] "i" (LAUNCH_FGO), 134 [LAUNCH_PC] "i" (offsetof(struct cpulaunch, pc)), 135 [LAUNCH_GP] "i" (offsetof(struct cpulaunch, gp)), 136 [LAUNCH_SP] "i" (offsetof(struct cpulaunch, sp)), 137 [LAUNCH_A0] "i" (offsetof(struct cpulaunch, a0)), 138 [LAUNCH_FLAGS] "i" (offsetof(struct cpulaunch, flags)), 139 [waitperiod] "i" ((cpufreq / 2) / 100) /* delay of ~10ms */ 140 : "ra","a0","a1","a2","a3","sp" /* ,"gp" */ 141 ); 142} 143