MIPS: Cavium: Add CPU hotplugging code.

Thanks to Cavium Inc. for the code contribution and help.

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

+365 -2
+2
arch/mips/Kconfig
··· 601 601 select SYS_SUPPORTS_64BIT_KERNEL 602 602 select SYS_SUPPORTS_BIG_ENDIAN 603 603 select SYS_SUPPORTS_HIGHMEM 604 + select SYS_SUPPORTS_HOTPLUG_CPU 604 605 select SYS_HAS_CPU_CAVIUM_OCTEON 605 606 help 606 607 The Octeon simulator is software performance model of the Cavium ··· 616 615 select SYS_SUPPORTS_64BIT_KERNEL 617 616 select SYS_SUPPORTS_BIG_ENDIAN 618 617 select SYS_SUPPORTS_HIGHMEM 618 + select SYS_SUPPORTS_HOTPLUG_CPU 619 619 select SYS_HAS_EARLY_PRINTK 620 620 select SYS_HAS_CPU_CAVIUM_OCTEON 621 621 select SWAP_IO_SPACE
+59
arch/mips/cavium-octeon/octeon-irq.c
··· 501 501 } 502 502 } 503 503 } 504 + 505 + #ifdef CONFIG_HOTPLUG_CPU 506 + static int is_irq_enabled_on_cpu(unsigned int irq, unsigned int cpu) 507 + { 508 + unsigned int isset; 509 + #ifdef CONFIG_SMP 510 + int coreid = cpu_logical_map(cpu); 511 + #else 512 + int coreid = cvmx_get_core_num(); 513 + #endif 514 + int bit = (irq < OCTEON_IRQ_WDOG0) ? 515 + irq - OCTEON_IRQ_WORKQ0 : irq - OCTEON_IRQ_WDOG0; 516 + if (irq < 64) { 517 + isset = (cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)) & 518 + (1ull << bit)) >> bit; 519 + } else { 520 + isset = (cvmx_read_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1)) & 521 + (1ull << bit)) >> bit; 522 + } 523 + return isset; 524 + } 525 + 526 + void fixup_irqs(void) 527 + { 528 + int irq; 529 + 530 + for (irq = OCTEON_IRQ_SW0; irq <= OCTEON_IRQ_TIMER; irq++) 531 + octeon_irq_core_disable_local(irq); 532 + 533 + for (irq = OCTEON_IRQ_WORKQ0; irq <= OCTEON_IRQ_GPIO15; irq++) { 534 + if (is_irq_enabled_on_cpu(irq, smp_processor_id())) { 535 + /* ciu irq migrates to next cpu */ 536 + octeon_irq_chip_ciu0.disable(irq); 537 + octeon_irq_ciu0_set_affinity(irq, &cpu_online_map); 538 + } 539 + } 540 + 541 + #if 0 542 + for (irq = OCTEON_IRQ_MBOX0; irq <= OCTEON_IRQ_MBOX1; irq++) 543 + octeon_irq_mailbox_mask(irq); 544 + #endif 545 + for (irq = OCTEON_IRQ_UART0; irq <= OCTEON_IRQ_BOOTDMA; irq++) { 546 + if (is_irq_enabled_on_cpu(irq, smp_processor_id())) { 547 + /* ciu irq migrates to next cpu */ 548 + octeon_irq_chip_ciu0.disable(irq); 549 + octeon_irq_ciu0_set_affinity(irq, &cpu_online_map); 550 + } 551 + } 552 + 553 + for (irq = OCTEON_IRQ_UART2; irq <= OCTEON_IRQ_RESERVED135; irq++) { 554 + if (is_irq_enabled_on_cpu(irq, smp_processor_id())) { 555 + /* ciu irq migrates to next cpu */ 556 + octeon_irq_chip_ciu1.disable(irq); 557 + octeon_irq_ciu1_set_affinity(irq, &cpu_online_map); 558 + } 559 + } 560 + } 561 + 562 + #endif /* CONFIG_HOTPLUG_CPU */
+70
arch/mips/cavium-octeon/octeon_boot.h
··· 1 + /* 2 + * (C) Copyright 2004, 2005 Cavium Networks 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 as 6 + * published by the Free Software Foundation; either version 2 of 7 + * the License, or (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, write to the Free Software 16 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 + * MA 02111-1307 USA 18 + */ 19 + 20 + #ifndef __OCTEON_BOOT_H__ 21 + #define __OCTEON_BOOT_H__ 22 + 23 + #include <linux/types.h> 24 + 25 + struct boot_init_vector { 26 + uint32_t stack_addr; 27 + uint32_t code_addr; 28 + uint32_t app_start_func_addr; 29 + uint32_t k0_val; 30 + uint32_t flags; 31 + uint32_t boot_info_addr; 32 + uint32_t pad; 33 + uint32_t pad2; 34 + }; 35 + 36 + /* similar to bootloader's linux_app_boot_info but without global data */ 37 + struct linux_app_boot_info { 38 + uint32_t labi_signature; 39 + uint32_t start_core0_addr; 40 + uint32_t avail_coremask; 41 + uint32_t pci_console_active; 42 + uint32_t icache_prefetch_disable; 43 + uint32_t InitTLBStart_addr; 44 + uint32_t start_app_addr; 45 + uint32_t cur_exception_base; 46 + uint32_t no_mark_private_data; 47 + uint32_t compact_flash_common_base_addr; 48 + uint32_t compact_flash_attribute_base_addr; 49 + uint32_t led_display_base_addr; 50 + }; 51 + 52 + /* If not to copy a lot of bootloader's structures 53 + here is only offset of requested member */ 54 + #define AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK 0x765c 55 + 56 + /* hardcoded in bootloader */ 57 + #define LABI_ADDR_IN_BOOTLOADER 0x700 58 + 59 + #define LINUX_APP_BOOT_BLOCK_NAME "linux-app-boot" 60 + 61 + #define LABI_SIGNATURE 0xAABBCCDD 62 + 63 + /* from uboot-headers/octeon_mem_map.h */ 64 + #define EXCEPTION_BASE_INCR (4 * 1024) 65 + /* Increment size for exception base addresses (4k minimum) */ 66 + #define EXCEPTION_BASE_BASE 0 67 + #define BOOTLOADER_PRIV_DATA_BASE (EXCEPTION_BASE_BASE + 0x800) 68 + #define BOOTLOADER_BOOT_VECTOR (BOOTLOADER_PRIV_DATA_BASE) 69 + 70 + #endif /* __OCTEON_BOOT_H__ */
+233 -1
arch/mips/cavium-octeon/smp.c
··· 5 5 * 6 6 * Copyright (C) 2004-2008 Cavium Networks 7 7 */ 8 + #include <linux/cpu.h> 8 9 #include <linux/init.h> 9 10 #include <linux/delay.h> 10 11 #include <linux/smp.h> ··· 20 19 21 20 #include <asm/octeon/octeon.h> 22 21 22 + #include "octeon_boot.h" 23 + 23 24 volatile unsigned long octeon_processor_boot = 0xff; 24 25 volatile unsigned long octeon_processor_sp; 25 26 volatile unsigned long octeon_processor_gp; 27 + 28 + #ifdef CONFIG_HOTPLUG_CPU 29 + static unsigned int InitTLBStart_addr; 30 + #endif 26 31 27 32 static irqreturn_t mailbox_interrupt(int irq, void *dev_id) 28 33 { ··· 74 67 } 75 68 76 69 /** 77 - * Detect available CPUs, populate phys_cpu_present_map 70 + * Detect available CPUs, populate cpu_possible_map 78 71 */ 72 + static void octeon_smp_hotplug_setup(void) 73 + { 74 + #ifdef CONFIG_HOTPLUG_CPU 75 + uint32_t labi_signature; 76 + 77 + labi_signature = 78 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 79 + LABI_ADDR_IN_BOOTLOADER + 80 + offsetof(struct linux_app_boot_info, 81 + labi_signature))); 82 + if (labi_signature != LABI_SIGNATURE) 83 + pr_err("The bootloader version on this board is incorrect\n"); 84 + InitTLBStart_addr = 85 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 86 + LABI_ADDR_IN_BOOTLOADER + 87 + offsetof(struct linux_app_boot_info, 88 + InitTLBStart_addr))); 89 + #endif 90 + } 91 + 79 92 static void octeon_smp_setup(void) 80 93 { 81 94 const int coreid = cvmx_get_core_num(); ··· 118 91 cpus++; 119 92 } 120 93 } 94 + cpu_present_map = cpu_possible_map; 95 + 96 + octeon_smp_hotplug_setup(); 121 97 } 122 98 123 99 /** ··· 158 128 const int coreid = cvmx_get_core_num(); 159 129 union cvmx_ciu_intx_sum0 interrupt_enable; 160 130 131 + #ifdef CONFIG_HOTPLUG_CPU 132 + unsigned int cur_exception_base; 133 + 134 + cur_exception_base = cvmx_read64_uint32( 135 + CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 136 + LABI_ADDR_IN_BOOTLOADER + 137 + offsetof(struct linux_app_boot_info, 138 + cur_exception_base))); 139 + /* cur_exception_base is incremented in bootloader after setting */ 140 + write_c0_ebase((unsigned int)(cur_exception_base - EXCEPTION_BASE_INCR)); 141 + #endif 161 142 octeon_check_cpu_bist(); 162 143 octeon_init_cvmcount(); 163 144 /* ··· 240 199 #endif 241 200 } 242 201 202 + #ifdef CONFIG_HOTPLUG_CPU 203 + 204 + /* State of each CPU. */ 205 + DEFINE_PER_CPU(int, cpu_state); 206 + 207 + extern void fixup_irqs(void); 208 + 209 + static DEFINE_SPINLOCK(smp_reserve_lock); 210 + 211 + static int octeon_cpu_disable(void) 212 + { 213 + unsigned int cpu = smp_processor_id(); 214 + 215 + if (cpu == 0) 216 + return -EBUSY; 217 + 218 + spin_lock(&smp_reserve_lock); 219 + 220 + cpu_clear(cpu, cpu_online_map); 221 + cpu_clear(cpu, cpu_callin_map); 222 + local_irq_disable(); 223 + fixup_irqs(); 224 + local_irq_enable(); 225 + 226 + flush_cache_all(); 227 + local_flush_tlb_all(); 228 + 229 + spin_unlock(&smp_reserve_lock); 230 + 231 + return 0; 232 + } 233 + 234 + static void octeon_cpu_die(unsigned int cpu) 235 + { 236 + int coreid = cpu_logical_map(cpu); 237 + uint32_t avail_coremask; 238 + struct cvmx_bootmem_named_block_desc *block_desc; 239 + 240 + #ifdef CONFIG_CAVIUM_OCTEON_WATCHDOG 241 + /* Disable the watchdog */ 242 + cvmx_ciu_wdogx_t ciu_wdog; 243 + ciu_wdog.u64 = cvmx_read_csr(CVMX_CIU_WDOGX(cpu)); 244 + ciu_wdog.s.mode = 0; 245 + cvmx_write_csr(CVMX_CIU_WDOGX(cpu), ciu_wdog.u64); 246 + #endif 247 + 248 + while (per_cpu(cpu_state, cpu) != CPU_DEAD) 249 + cpu_relax(); 250 + 251 + /* 252 + * This is a bit complicated strategics of getting/settig available 253 + * cores mask, copied from bootloader 254 + */ 255 + /* LINUX_APP_BOOT_BLOCK is initialized in bootoct binary */ 256 + block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME); 257 + 258 + if (!block_desc) { 259 + avail_coremask = 260 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 261 + LABI_ADDR_IN_BOOTLOADER + 262 + offsetof 263 + (struct linux_app_boot_info, 264 + avail_coremask))); 265 + } else { /* alternative, already initialized */ 266 + avail_coremask = 267 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 268 + block_desc->base_addr + 269 + AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK)); 270 + } 271 + 272 + avail_coremask |= 1 << coreid; 273 + 274 + /* Setting avail_coremask for bootoct binary */ 275 + if (!block_desc) { 276 + cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 277 + LABI_ADDR_IN_BOOTLOADER + 278 + offsetof(struct linux_app_boot_info, 279 + avail_coremask)), 280 + avail_coremask); 281 + } else { 282 + cvmx_write64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 283 + block_desc->base_addr + 284 + AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK), 285 + avail_coremask); 286 + } 287 + 288 + pr_info("Reset core %d. Available Coremask = %x \n", coreid, 289 + avail_coremask); 290 + cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid); 291 + cvmx_write_csr(CVMX_CIU_PP_RST, 0); 292 + } 293 + 294 + void play_dead(void) 295 + { 296 + int coreid = cvmx_get_core_num(); 297 + 298 + idle_task_exit(); 299 + octeon_processor_boot = 0xff; 300 + per_cpu(cpu_state, coreid) = CPU_DEAD; 301 + 302 + while (1) /* core will be reset here */ 303 + ; 304 + } 305 + 306 + extern void kernel_entry(unsigned long arg1, ...); 307 + 308 + static void start_after_reset(void) 309 + { 310 + kernel_entry(0, 0, 0); /* set a2 = 0 for secondary core */ 311 + } 312 + 313 + int octeon_update_boot_vector(unsigned int cpu) 314 + { 315 + 316 + int coreid = cpu_logical_map(cpu); 317 + unsigned int avail_coremask; 318 + struct cvmx_bootmem_named_block_desc *block_desc; 319 + struct boot_init_vector *boot_vect = 320 + (struct boot_init_vector *) cvmx_phys_to_ptr(0x0 + 321 + BOOTLOADER_BOOT_VECTOR); 322 + 323 + block_desc = cvmx_bootmem_find_named_block(LINUX_APP_BOOT_BLOCK_NAME); 324 + 325 + if (!block_desc) { 326 + avail_coremask = 327 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 328 + LABI_ADDR_IN_BOOTLOADER + 329 + offsetof(struct linux_app_boot_info, 330 + avail_coremask))); 331 + } else { /* alternative, already initialized */ 332 + avail_coremask = 333 + cvmx_read64_uint32(CVMX_ADD_SEG(CVMX_MIPS_SPACE_XKPHYS, 334 + block_desc->base_addr + 335 + AVAIL_COREMASK_OFFSET_IN_LINUX_APP_BOOT_BLOCK)); 336 + } 337 + 338 + if (!(avail_coremask & (1 << coreid))) { 339 + /* core not available, assume, that catched by simple-executive */ 340 + cvmx_write_csr(CVMX_CIU_PP_RST, 1 << coreid); 341 + cvmx_write_csr(CVMX_CIU_PP_RST, 0); 342 + } 343 + 344 + boot_vect[coreid].app_start_func_addr = 345 + (uint32_t) (unsigned long) start_after_reset; 346 + boot_vect[coreid].code_addr = InitTLBStart_addr; 347 + 348 + CVMX_SYNC; 349 + 350 + cvmx_write_csr(CVMX_CIU_NMI, (1 << coreid) & avail_coremask); 351 + 352 + return 0; 353 + } 354 + 355 + static int __cpuinit octeon_cpu_callback(struct notifier_block *nfb, 356 + unsigned long action, void *hcpu) 357 + { 358 + unsigned int cpu = (unsigned long)hcpu; 359 + 360 + switch (action) { 361 + case CPU_UP_PREPARE: 362 + octeon_update_boot_vector(cpu); 363 + break; 364 + case CPU_ONLINE: 365 + pr_info("Cpu %d online\n", cpu); 366 + break; 367 + case CPU_DEAD: 368 + break; 369 + } 370 + 371 + return NOTIFY_OK; 372 + } 373 + 374 + static struct notifier_block __cpuinitdata octeon_cpu_notifier = { 375 + .notifier_call = octeon_cpu_callback, 376 + }; 377 + 378 + static int __cpuinit register_cavium_notifier(void) 379 + { 380 + register_hotcpu_notifier(&octeon_cpu_notifier); 381 + 382 + return 0; 383 + } 384 + 385 + late_initcall(register_cavium_notifier); 386 + 387 + #endif /* CONFIG_HOTPLUG_CPU */ 388 + 243 389 struct plat_smp_ops octeon_smp_ops = { 244 390 .send_ipi_single = octeon_send_ipi_single, 245 391 .send_ipi_mask = octeon_send_ipi_mask, ··· 436 208 .boot_secondary = octeon_boot_secondary, 437 209 .smp_setup = octeon_smp_setup, 438 210 .prepare_cpus = octeon_prepare_cpus, 211 + #ifdef CONFIG_HOTPLUG_CPU 212 + .cpu_disable = octeon_cpu_disable, 213 + .cpu_die = octeon_cpu_die, 214 + #endif 439 215 };
+1 -1
arch/mips/include/asm/smp.h
··· 41 41 /* Octeon - Tell another core to flush its icache */ 42 42 #define SMP_ICACHE_FLUSH 0x4 43 43 44 - extern cpumask_t cpu_callin_map; 44 + extern volatile cpumask_t cpu_callin_map; 45 45 46 46 extern void asmlinkage smp_bootstrap(void); 47 47