1/* 2 * Copyright 2010 Tilera Corporation. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, version 2. 7 * 8 * This program is distributed in the hope that it will be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 11 * NON INFRINGEMENT. See the GNU General Public License for 12 * more details. 13 * 14 * TILE SMP support routines. 15 */ 16 17#include <linux/smp.h> 18#include <linux/interrupt.h> 19#include <linux/io.h> 20#include <linux/irq.h> 21#include <linux/module.h> 22#include <asm/cacheflush.h> 23 24HV_Topology smp_topology __write_once; 25EXPORT_SYMBOL(smp_topology); 26 27#if CHIP_HAS_IPI() 28static unsigned long __iomem *ipi_mappings[NR_CPUS]; 29#endif 30 31 32/* 33 * Top-level send_IPI*() functions to send messages to other cpus. 34 */ 35 36/* Set by smp_send_stop() to avoid recursive panics. */ 37static int stopping_cpus; 38 39void send_IPI_single(int cpu, int tag) 40{ 41 HV_Recipient recip = { 42 .y = cpu / smp_width, 43 .x = cpu % smp_width, 44 .state = HV_TO_BE_SENT 45 }; 46 int rc = hv_send_message(&recip, 1, (HV_VirtAddr)&tag, sizeof(tag)); 47 BUG_ON(rc <= 0); 48} 49 50void send_IPI_many(const struct cpumask *mask, int tag) 51{ 52 HV_Recipient recip[NR_CPUS]; 53 int cpu, sent; 54 int nrecip = 0; 55 int my_cpu = smp_processor_id(); 56 for_each_cpu(cpu, mask) { 57 HV_Recipient *r; 58 BUG_ON(cpu == my_cpu); 59 r = &recip[nrecip++]; 60 r->y = cpu / smp_width; 61 r->x = cpu % smp_width; 62 r->state = HV_TO_BE_SENT; 63 } 64 sent = 0; 65 while (sent < nrecip) { 66 int rc = hv_send_message(recip, nrecip, 67 (HV_VirtAddr)&tag, sizeof(tag)); 68 if (rc <= 0) { 69 if (!stopping_cpus) /* avoid recursive panic */ 70 panic("hv_send_message returned %d", rc); 71 break; 72 } 73 sent += rc; 74 } 75} 76 77void send_IPI_allbutself(int tag) 78{ 79 struct cpumask mask; 80 cpumask_copy(&mask, cpu_online_mask); 81 cpumask_clear_cpu(smp_processor_id(), &mask); 82 send_IPI_many(&mask, tag); 83} 84 85 86/* 87 * Provide smp_call_function_mask, but also run function locally 88 * if specified in the mask. 89 */ 90void on_each_cpu_mask(const struct cpumask *mask, void (*func)(void *), 91 void *info, bool wait) 92{ 93 int cpu = get_cpu(); 94 smp_call_function_many(mask, func, info, wait); 95 if (cpumask_test_cpu(cpu, mask)) { 96 local_irq_disable(); 97 func(info); 98 local_irq_enable(); 99 } 100 put_cpu(); 101} 102 103 104/* 105 * Functions related to starting/stopping cpus. 106 */ 107 108/* Handler to start the current cpu. */ 109static void smp_start_cpu_interrupt(void) 110{ 111 get_irq_regs()->pc = start_cpu_function_addr; 112} 113 114/* Handler to stop the current cpu. */ 115static void smp_stop_cpu_interrupt(void) 116{ 117 set_cpu_online(smp_processor_id(), 0); 118 raw_local_irq_disable_all(); 119 for (;;) 120 asm("nap"); 121} 122 123/* This function calls the 'stop' function on all other CPUs in the system. */ 124void smp_send_stop(void) 125{ 126 stopping_cpus = 1; 127 send_IPI_allbutself(MSG_TAG_STOP_CPU); 128} 129 130 131/* 132 * Dispatch code called from hv_message_intr() for HV_MSG_TILE hv messages. 133 */ 134void evaluate_message(int tag) 135{ 136 switch (tag) { 137 case MSG_TAG_START_CPU: /* Start up a cpu */ 138 smp_start_cpu_interrupt(); 139 break; 140 141 case MSG_TAG_STOP_CPU: /* Sent to shut down slave CPU's */ 142 smp_stop_cpu_interrupt(); 143 break; 144 145 case MSG_TAG_CALL_FUNCTION_MANY: /* Call function on cpumask */ 146 generic_smp_call_function_interrupt(); 147 break; 148 149 case MSG_TAG_CALL_FUNCTION_SINGLE: /* Call function on one other CPU */ 150 generic_smp_call_function_single_interrupt(); 151 break; 152 153 default: 154 panic("Unknown IPI message tag %d", tag); 155 break; 156 } 157} 158 159 160/* 161 * flush_icache_range() code uses smp_call_function(). 162 */ 163 164struct ipi_flush { 165 unsigned long start; 166 unsigned long end; 167}; 168 169static void ipi_flush_icache_range(void *info) 170{ 171 struct ipi_flush *flush = (struct ipi_flush *) info; 172 __flush_icache_range(flush->start, flush->end); 173} 174 175void flush_icache_range(unsigned long start, unsigned long end) 176{ 177 struct ipi_flush flush = { start, end }; 178 preempt_disable(); 179 on_each_cpu(ipi_flush_icache_range, &flush, 1); 180 preempt_enable(); 181} 182 183 184/* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */ 185static irqreturn_t handle_reschedule_ipi(int irq, void *token) 186{ 187 /* 188 * Nothing to do here; when we return from interrupt, the 189 * rescheduling will occur there. But do bump the interrupt 190 * profiler count in the meantime. 191 */ 192 __get_cpu_var(irq_stat).irq_resched_count++; 193 194 return IRQ_HANDLED; 195} 196 197static struct irqaction resched_action = { 198 .handler = handle_reschedule_ipi, 199 .name = "resched", 200 .dev_id = handle_reschedule_ipi /* unique token */, 201}; 202 203void __init ipi_init(void) 204{ 205#if CHIP_HAS_IPI() 206 int cpu; 207 /* Map IPI trigger MMIO addresses. */ 208 for_each_possible_cpu(cpu) { 209 HV_Coord tile; 210 HV_PTE pte; 211 unsigned long offset; 212 213 tile.x = cpu_x(cpu); 214 tile.y = cpu_y(cpu); 215 if (hv_get_ipi_pte(tile, 1, &pte) != 0) 216 panic("Failed to initialize IPI for cpu %d\n", cpu); 217 218 offset = hv_pte_get_pfn(pte) << PAGE_SHIFT; 219 ipi_mappings[cpu] = ioremap_prot(offset, PAGE_SIZE, pte); 220 } 221#endif 222 223 /* Bind handle_reschedule_ipi() to IRQ_RESCHEDULE. */ 224 tile_irq_activate(IRQ_RESCHEDULE, TILE_IRQ_PERCPU); 225 BUG_ON(setup_irq(IRQ_RESCHEDULE, &resched_action)); 226} 227 228#if CHIP_HAS_IPI() 229 230void smp_send_reschedule(int cpu) 231{ 232 WARN_ON(cpu_is_offline(cpu)); 233 234 /* 235 * We just want to do an MMIO store. The traditional writeq() 236 * functions aren't really correct here, since they're always 237 * directed at the PCI shim. For now, just do a raw store, 238 * casting away the __iomem attribute. 239 */ 240 ((unsigned long __force *)ipi_mappings[cpu])[IRQ_RESCHEDULE] = 0; 241} 242 243#else 244 245void smp_send_reschedule(int cpu) 246{ 247 HV_Coord coord; 248 249 WARN_ON(cpu_is_offline(cpu)); 250 251 coord.y = cpu_y(cpu); 252 coord.x = cpu_x(cpu); 253 hv_trigger_ipi(coord, IRQ_RESCHEDULE); 254} 255 256#endif /* CHIP_HAS_IPI() */ 257