| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 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> | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 18 | #include <linux/interrupt.h> | 
|  | 19 | #include <linux/io.h> | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 20 | #include <linux/irq.h> | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 21 | #include <linux/module.h> | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 22 | #include <asm/cacheflush.h> | 
|  | 23 |  | 
|  | 24 | HV_Topology smp_topology __write_once; | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 25 | EXPORT_SYMBOL(smp_topology); | 
|  | 26 |  | 
|  | 27 | #if CHIP_HAS_IPI() | 
|  | 28 | static unsigned long __iomem *ipi_mappings[NR_CPUS]; | 
|  | 29 | #endif | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 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. */ | 
|  | 37 | static int stopping_cpus; | 
|  | 38 |  | 
| Chris Metcalf | bbeee4b2 | 2011-02-28 13:32:14 -0500 | [diff] [blame] | 39 | static void __send_IPI_many(HV_Recipient *recip, int nrecip, int tag) | 
|  | 40 | { | 
|  | 41 | int sent = 0; | 
|  | 42 | while (sent < nrecip) { | 
|  | 43 | int rc = hv_send_message(recip, nrecip, | 
|  | 44 | (HV_VirtAddr)&tag, sizeof(tag)); | 
|  | 45 | if (rc < 0) { | 
|  | 46 | if (!stopping_cpus)  /* avoid recursive panic */ | 
|  | 47 | panic("hv_send_message returned %d", rc); | 
|  | 48 | break; | 
|  | 49 | } | 
|  | 50 | WARN_ONCE(rc == 0, "hv_send_message() returned zero\n"); | 
|  | 51 | sent += rc; | 
|  | 52 | } | 
|  | 53 | } | 
|  | 54 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 55 | void send_IPI_single(int cpu, int tag) | 
|  | 56 | { | 
|  | 57 | HV_Recipient recip = { | 
|  | 58 | .y = cpu / smp_width, | 
|  | 59 | .x = cpu % smp_width, | 
|  | 60 | .state = HV_TO_BE_SENT | 
|  | 61 | }; | 
| Chris Metcalf | bbeee4b2 | 2011-02-28 13:32:14 -0500 | [diff] [blame] | 62 | __send_IPI_many(&recip, 1, tag); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 63 | } | 
|  | 64 |  | 
|  | 65 | void send_IPI_many(const struct cpumask *mask, int tag) | 
|  | 66 | { | 
|  | 67 | HV_Recipient recip[NR_CPUS]; | 
| Chris Metcalf | bbeee4b2 | 2011-02-28 13:32:14 -0500 | [diff] [blame] | 68 | int cpu; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 69 | int nrecip = 0; | 
|  | 70 | int my_cpu = smp_processor_id(); | 
|  | 71 | for_each_cpu(cpu, mask) { | 
|  | 72 | HV_Recipient *r; | 
|  | 73 | BUG_ON(cpu == my_cpu); | 
|  | 74 | r = &recip[nrecip++]; | 
|  | 75 | r->y = cpu / smp_width; | 
|  | 76 | r->x = cpu % smp_width; | 
|  | 77 | r->state = HV_TO_BE_SENT; | 
|  | 78 | } | 
| Chris Metcalf | bbeee4b2 | 2011-02-28 13:32:14 -0500 | [diff] [blame] | 79 | __send_IPI_many(recip, nrecip, tag); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 80 | } | 
|  | 81 |  | 
|  | 82 | void send_IPI_allbutself(int tag) | 
|  | 83 | { | 
|  | 84 | struct cpumask mask; | 
|  | 85 | cpumask_copy(&mask, cpu_online_mask); | 
|  | 86 | cpumask_clear_cpu(smp_processor_id(), &mask); | 
|  | 87 | send_IPI_many(&mask, tag); | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 |  | 
|  | 91 | /* | 
|  | 92 | * Provide smp_call_function_mask, but also run function locally | 
|  | 93 | * if specified in the mask. | 
|  | 94 | */ | 
|  | 95 | void on_each_cpu_mask(const struct cpumask *mask, void (*func)(void *), | 
|  | 96 | void *info, bool wait) | 
|  | 97 | { | 
|  | 98 | int cpu = get_cpu(); | 
|  | 99 | smp_call_function_many(mask, func, info, wait); | 
|  | 100 | if (cpumask_test_cpu(cpu, mask)) { | 
|  | 101 | local_irq_disable(); | 
|  | 102 | func(info); | 
|  | 103 | local_irq_enable(); | 
|  | 104 | } | 
|  | 105 | put_cpu(); | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 |  | 
|  | 109 | /* | 
|  | 110 | * Functions related to starting/stopping cpus. | 
|  | 111 | */ | 
|  | 112 |  | 
|  | 113 | /* Handler to start the current cpu. */ | 
|  | 114 | static void smp_start_cpu_interrupt(void) | 
|  | 115 | { | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 116 | get_irq_regs()->pc = start_cpu_function_addr; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | /* Handler to stop the current cpu. */ | 
|  | 120 | static void smp_stop_cpu_interrupt(void) | 
|  | 121 | { | 
|  | 122 | set_cpu_online(smp_processor_id(), 0); | 
| Chris Metcalf | 5d96611 | 2010-11-01 15:24:29 -0400 | [diff] [blame] | 123 | arch_local_irq_disable_all(); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 124 | for (;;) | 
|  | 125 | asm("nap"); | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | /* This function calls the 'stop' function on all other CPUs in the system. */ | 
|  | 129 | void smp_send_stop(void) | 
|  | 130 | { | 
|  | 131 | stopping_cpus = 1; | 
|  | 132 | send_IPI_allbutself(MSG_TAG_STOP_CPU); | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 |  | 
|  | 136 | /* | 
|  | 137 | * Dispatch code called from hv_message_intr() for HV_MSG_TILE hv messages. | 
|  | 138 | */ | 
|  | 139 | void evaluate_message(int tag) | 
|  | 140 | { | 
|  | 141 | switch (tag) { | 
|  | 142 | case MSG_TAG_START_CPU: /* Start up a cpu */ | 
|  | 143 | smp_start_cpu_interrupt(); | 
|  | 144 | break; | 
|  | 145 |  | 
|  | 146 | case MSG_TAG_STOP_CPU: /* Sent to shut down slave CPU's */ | 
|  | 147 | smp_stop_cpu_interrupt(); | 
|  | 148 | break; | 
|  | 149 |  | 
|  | 150 | case MSG_TAG_CALL_FUNCTION_MANY: /* Call function on cpumask */ | 
|  | 151 | generic_smp_call_function_interrupt(); | 
|  | 152 | break; | 
|  | 153 |  | 
|  | 154 | case MSG_TAG_CALL_FUNCTION_SINGLE: /* Call function on one other CPU */ | 
|  | 155 | generic_smp_call_function_single_interrupt(); | 
|  | 156 | break; | 
|  | 157 |  | 
|  | 158 | default: | 
|  | 159 | panic("Unknown IPI message tag %d", tag); | 
|  | 160 | break; | 
|  | 161 | } | 
|  | 162 | } | 
|  | 163 |  | 
|  | 164 |  | 
|  | 165 | /* | 
|  | 166 | * flush_icache_range() code uses smp_call_function(). | 
|  | 167 | */ | 
|  | 168 |  | 
|  | 169 | struct ipi_flush { | 
|  | 170 | unsigned long start; | 
|  | 171 | unsigned long end; | 
|  | 172 | }; | 
|  | 173 |  | 
|  | 174 | static void ipi_flush_icache_range(void *info) | 
|  | 175 | { | 
|  | 176 | struct ipi_flush *flush = (struct ipi_flush *) info; | 
|  | 177 | __flush_icache_range(flush->start, flush->end); | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | void flush_icache_range(unsigned long start, unsigned long end) | 
|  | 181 | { | 
|  | 182 | struct ipi_flush flush = { start, end }; | 
|  | 183 | preempt_disable(); | 
|  | 184 | on_each_cpu(ipi_flush_icache_range, &flush, 1); | 
|  | 185 | preempt_enable(); | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 |  | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 189 | /* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */ | 
|  | 190 | static irqreturn_t handle_reschedule_ipi(int irq, void *token) | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 191 | { | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 192 | __get_cpu_var(irq_stat).irq_resched_count++; | 
| Peter Zijlstra | 184748c | 2011-04-05 17:23:39 +0200 | [diff] [blame] | 193 | scheduler_ipi(); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 194 |  | 
|  | 195 | return IRQ_HANDLED; | 
|  | 196 | } | 
|  | 197 |  | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 198 | static struct irqaction resched_action = { | 
|  | 199 | .handler = handle_reschedule_ipi, | 
|  | 200 | .name = "resched", | 
|  | 201 | .dev_id = handle_reschedule_ipi /* unique token */, | 
|  | 202 | }; | 
|  | 203 |  | 
|  | 204 | void __init ipi_init(void) | 
|  | 205 | { | 
|  | 206 | #if CHIP_HAS_IPI() | 
|  | 207 | int cpu; | 
|  | 208 | /* Map IPI trigger MMIO addresses. */ | 
|  | 209 | for_each_possible_cpu(cpu) { | 
|  | 210 | HV_Coord tile; | 
|  | 211 | HV_PTE pte; | 
|  | 212 | unsigned long offset; | 
|  | 213 |  | 
|  | 214 | tile.x = cpu_x(cpu); | 
|  | 215 | tile.y = cpu_y(cpu); | 
| Chris Metcalf | a78c942 | 2010-10-14 16:23:03 -0400 | [diff] [blame] | 216 | if (hv_get_ipi_pte(tile, KERNEL_PL, &pte) != 0) | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 217 | panic("Failed to initialize IPI for cpu %d\n", cpu); | 
|  | 218 |  | 
|  | 219 | offset = hv_pte_get_pfn(pte) << PAGE_SHIFT; | 
|  | 220 | ipi_mappings[cpu] = ioremap_prot(offset, PAGE_SIZE, pte); | 
|  | 221 | } | 
|  | 222 | #endif | 
|  | 223 |  | 
|  | 224 | /* Bind handle_reschedule_ipi() to IRQ_RESCHEDULE. */ | 
|  | 225 | tile_irq_activate(IRQ_RESCHEDULE, TILE_IRQ_PERCPU); | 
|  | 226 | BUG_ON(setup_irq(IRQ_RESCHEDULE, &resched_action)); | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | #if CHIP_HAS_IPI() | 
|  | 230 |  | 
|  | 231 | void smp_send_reschedule(int cpu) | 
|  | 232 | { | 
|  | 233 | WARN_ON(cpu_is_offline(cpu)); | 
|  | 234 |  | 
|  | 235 | /* | 
|  | 236 | * We just want to do an MMIO store.  The traditional writeq() | 
|  | 237 | * functions aren't really correct here, since they're always | 
|  | 238 | * directed at the PCI shim.  For now, just do a raw store, | 
|  | 239 | * casting away the __iomem attribute. | 
|  | 240 | */ | 
|  | 241 | ((unsigned long __force *)ipi_mappings[cpu])[IRQ_RESCHEDULE] = 0; | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | #else | 
|  | 245 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 246 | void smp_send_reschedule(int cpu) | 
|  | 247 | { | 
|  | 248 | HV_Coord coord; | 
|  | 249 |  | 
|  | 250 | WARN_ON(cpu_is_offline(cpu)); | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 251 |  | 
|  | 252 | coord.y = cpu_y(cpu); | 
|  | 253 | coord.x = cpu_x(cpu); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 254 | hv_trigger_ipi(coord, IRQ_RESCHEDULE); | 
|  | 255 | } | 
| Chris Metcalf | fb702b9 | 2010-06-25 16:41:11 -0400 | [diff] [blame] | 256 |  | 
|  | 257 | #endif /* CHIP_HAS_IPI() */ |