108dbd0f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
243afdf50SRichard Kuo /*
343afdf50SRichard Kuo * SMP support for Hexagon
443afdf50SRichard Kuo *
5e1858b2aSRichard Kuo * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
643afdf50SRichard Kuo */
743afdf50SRichard Kuo
843afdf50SRichard Kuo #include <linux/err.h>
943afdf50SRichard Kuo #include <linux/errno.h>
1043afdf50SRichard Kuo #include <linux/kernel.h>
1143afdf50SRichard Kuo #include <linux/init.h>
1243afdf50SRichard Kuo #include <linux/interrupt.h>
1343afdf50SRichard Kuo #include <linux/module.h>
1443afdf50SRichard Kuo #include <linux/percpu.h>
1568e21be2SIngo Molnar #include <linux/sched/mm.h>
1643afdf50SRichard Kuo #include <linux/smp.h>
1743afdf50SRichard Kuo #include <linux/spinlock.h>
18e00574b7SRichard Kuo #include <linux/cpu.h>
19589ee628SIngo Molnar #include <linux/mm_types.h>
2043afdf50SRichard Kuo
2143afdf50SRichard Kuo #include <asm/time.h> /* timer_interrupt */
2243afdf50SRichard Kuo #include <asm/hexagon_vm.h>
2343afdf50SRichard Kuo
2443afdf50SRichard Kuo #define BASE_IPI_IRQ 26
2543afdf50SRichard Kuo
2643afdf50SRichard Kuo /*
270b5f9c00SRusty Russell * cpu_possible_mask needs to be filled out prior to setup_per_cpu_areas
2843afdf50SRichard Kuo * (which is prior to any of our smp_prepare_cpu crap), in order to set
2943afdf50SRichard Kuo * up the... per_cpu areas.
3043afdf50SRichard Kuo */
3143afdf50SRichard Kuo
3243afdf50SRichard Kuo struct ipi_data {
3343afdf50SRichard Kuo unsigned long bits;
3443afdf50SRichard Kuo };
3543afdf50SRichard Kuo
3643afdf50SRichard Kuo static DEFINE_PER_CPU(struct ipi_data, ipi_data);
3743afdf50SRichard Kuo
__handle_ipi(unsigned long * ops,struct ipi_data * ipi,int cpu)3843afdf50SRichard Kuo static inline void __handle_ipi(unsigned long *ops, struct ipi_data *ipi,
3943afdf50SRichard Kuo int cpu)
4043afdf50SRichard Kuo {
4143afdf50SRichard Kuo unsigned long msg = 0;
4243afdf50SRichard Kuo do {
4343afdf50SRichard Kuo msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
4443afdf50SRichard Kuo
4543afdf50SRichard Kuo switch (msg) {
4643afdf50SRichard Kuo
4743afdf50SRichard Kuo case IPI_TIMER:
4843afdf50SRichard Kuo ipi_timer();
4943afdf50SRichard Kuo break;
5043afdf50SRichard Kuo
5143afdf50SRichard Kuo case IPI_CALL_FUNC:
5243afdf50SRichard Kuo generic_smp_call_function_interrupt();
5343afdf50SRichard Kuo break;
5443afdf50SRichard Kuo
5543afdf50SRichard Kuo case IPI_CPU_STOP:
5643afdf50SRichard Kuo /*
5743afdf50SRichard Kuo * call vmstop()
5843afdf50SRichard Kuo */
5943afdf50SRichard Kuo __vmstop();
6043afdf50SRichard Kuo break;
6143afdf50SRichard Kuo
6243afdf50SRichard Kuo case IPI_RESCHEDULE:
6343afdf50SRichard Kuo scheduler_ipi();
6443afdf50SRichard Kuo break;
6543afdf50SRichard Kuo }
6643afdf50SRichard Kuo } while (msg < BITS_PER_LONG);
6743afdf50SRichard Kuo }
6843afdf50SRichard Kuo
6943afdf50SRichard Kuo /* Used for IPI call from other CPU's to unmask int */
smp_vm_unmask_irq(void * info)7043afdf50SRichard Kuo void smp_vm_unmask_irq(void *info)
7143afdf50SRichard Kuo {
7243afdf50SRichard Kuo __vmintop_locen((long) info);
7343afdf50SRichard Kuo }
7443afdf50SRichard Kuo
7543afdf50SRichard Kuo
7643afdf50SRichard Kuo /*
7743afdf50SRichard Kuo * This is based on Alpha's IPI stuff.
7843afdf50SRichard Kuo * Supposed to take (int, void*) as args now.
7943afdf50SRichard Kuo * Specifically, first arg is irq, second is the irq_desc.
8043afdf50SRichard Kuo */
8143afdf50SRichard Kuo
handle_ipi(int irq,void * desc)82*ef14250eSNathan Chancellor static irqreturn_t handle_ipi(int irq, void *desc)
8343afdf50SRichard Kuo {
8443afdf50SRichard Kuo int cpu = smp_processor_id();
8543afdf50SRichard Kuo struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
8643afdf50SRichard Kuo unsigned long ops;
8743afdf50SRichard Kuo
8843afdf50SRichard Kuo while ((ops = xchg(&ipi->bits, 0)) != 0)
8943afdf50SRichard Kuo __handle_ipi(&ops, ipi, cpu);
9043afdf50SRichard Kuo return IRQ_HANDLED;
9143afdf50SRichard Kuo }
9243afdf50SRichard Kuo
send_ipi(const struct cpumask * cpumask,enum ipi_message_type msg)9343afdf50SRichard Kuo void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg)
9443afdf50SRichard Kuo {
9543afdf50SRichard Kuo unsigned long flags;
9643afdf50SRichard Kuo unsigned long cpu;
9743afdf50SRichard Kuo unsigned long retval;
9843afdf50SRichard Kuo
9943afdf50SRichard Kuo local_irq_save(flags);
10043afdf50SRichard Kuo
10143afdf50SRichard Kuo for_each_cpu(cpu, cpumask) {
10243afdf50SRichard Kuo struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
10343afdf50SRichard Kuo
10443afdf50SRichard Kuo set_bit(msg, &ipi->bits);
10543afdf50SRichard Kuo /* Possible barrier here */
10643afdf50SRichard Kuo retval = __vmintop_post(BASE_IPI_IRQ+cpu);
10743afdf50SRichard Kuo
10843afdf50SRichard Kuo if (retval != 0) {
10943afdf50SRichard Kuo printk(KERN_ERR "interrupt %ld not configured?\n",
11043afdf50SRichard Kuo BASE_IPI_IRQ+cpu);
11143afdf50SRichard Kuo }
11243afdf50SRichard Kuo }
11343afdf50SRichard Kuo
11443afdf50SRichard Kuo local_irq_restore(flags);
11543afdf50SRichard Kuo }
11643afdf50SRichard Kuo
11743afdf50SRichard Kuo /*
11843afdf50SRichard Kuo * interrupts should already be disabled from the VM
11943afdf50SRichard Kuo * SP should already be correct; need to set THREADINFO_REG
12043afdf50SRichard Kuo * to point to current thread info
12143afdf50SRichard Kuo */
12243afdf50SRichard Kuo
start_secondary(void)123*ef14250eSNathan Chancellor static void start_secondary(void)
12443afdf50SRichard Kuo {
12543afdf50SRichard Kuo unsigned long thread_ptr;
12645b26ddeSafzal mohammed unsigned int cpu, irq;
12743afdf50SRichard Kuo
12843afdf50SRichard Kuo /* Calculate thread_info pointer from stack pointer */
12943afdf50SRichard Kuo __asm__ __volatile__(
13043afdf50SRichard Kuo "%0 = SP;\n"
13143afdf50SRichard Kuo : "=r" (thread_ptr)
13243afdf50SRichard Kuo );
13343afdf50SRichard Kuo
13443afdf50SRichard Kuo thread_ptr = thread_ptr & ~(THREAD_SIZE-1);
13543afdf50SRichard Kuo
13643afdf50SRichard Kuo __asm__ __volatile__(
13743afdf50SRichard Kuo QUOTED_THREADINFO_REG " = %0;\n"
13843afdf50SRichard Kuo :
13943afdf50SRichard Kuo : "r" (thread_ptr)
14043afdf50SRichard Kuo );
14143afdf50SRichard Kuo
14243afdf50SRichard Kuo /* Set the memory struct */
143f1f10076SVegard Nossum mmgrab(&init_mm);
14443afdf50SRichard Kuo current->active_mm = &init_mm;
14543afdf50SRichard Kuo
14643afdf50SRichard Kuo cpu = smp_processor_id();
14743afdf50SRichard Kuo
14845b26ddeSafzal mohammed irq = BASE_IPI_IRQ + cpu;
14945b26ddeSafzal mohammed if (request_irq(irq, handle_ipi, IRQF_TRIGGER_RISING, "ipi_handler",
15045b26ddeSafzal mohammed NULL))
15145b26ddeSafzal mohammed pr_err("Failed to request irq %u (ipi_handler)\n", irq);
15243afdf50SRichard Kuo
15343afdf50SRichard Kuo /* Register the clock_event dummy */
15443afdf50SRichard Kuo setup_percpu_clockdev();
15543afdf50SRichard Kuo
15643afdf50SRichard Kuo printk(KERN_INFO "%s cpu %d\n", __func__, current_thread_info()->cpu);
15743afdf50SRichard Kuo
15857f27ccaSSrivatsa S. Bhat notify_cpu_starting(cpu);
15957f27ccaSSrivatsa S. Bhat
16043afdf50SRichard Kuo set_cpu_online(cpu, true);
16157f27ccaSSrivatsa S. Bhat
16243afdf50SRichard Kuo local_irq_enable();
16343afdf50SRichard Kuo
164fc6d73d6SThomas Gleixner cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
16543afdf50SRichard Kuo }
16643afdf50SRichard Kuo
16743afdf50SRichard Kuo
16843afdf50SRichard Kuo /*
16943afdf50SRichard Kuo * called once for each present cpu
17043afdf50SRichard Kuo * apparently starts up the CPU and then
17143afdf50SRichard Kuo * maintains control until "cpu_online(cpu)" is set.
17243afdf50SRichard Kuo */
17343afdf50SRichard Kuo
__cpu_up(unsigned int cpu,struct task_struct * idle)1747ddc8399SPaul Gortmaker int __cpu_up(unsigned int cpu, struct task_struct *idle)
17543afdf50SRichard Kuo {
1763b0132ceSThomas Gleixner struct thread_info *thread = (struct thread_info *)idle->stack;
17743afdf50SRichard Kuo void *stack_start;
17843afdf50SRichard Kuo
17943afdf50SRichard Kuo thread->cpu = cpu;
18043afdf50SRichard Kuo
18143afdf50SRichard Kuo /* Boot to the head. */
18243afdf50SRichard Kuo stack_start = ((void *) thread) + THREAD_SIZE;
18343afdf50SRichard Kuo __vmstart(start_secondary, stack_start);
18443afdf50SRichard Kuo
1850b5f9c00SRusty Russell while (!cpu_online(cpu))
18643afdf50SRichard Kuo barrier();
18743afdf50SRichard Kuo
18843afdf50SRichard Kuo return 0;
18943afdf50SRichard Kuo }
19043afdf50SRichard Kuo
smp_cpus_done(unsigned int max_cpus)19143afdf50SRichard Kuo void __init smp_cpus_done(unsigned int max_cpus)
19243afdf50SRichard Kuo {
19343afdf50SRichard Kuo }
19443afdf50SRichard Kuo
smp_prepare_cpus(unsigned int max_cpus)19543afdf50SRichard Kuo void __init smp_prepare_cpus(unsigned int max_cpus)
19643afdf50SRichard Kuo {
19745b26ddeSafzal mohammed int i, irq = BASE_IPI_IRQ;
19843afdf50SRichard Kuo
19943afdf50SRichard Kuo /*
20043afdf50SRichard Kuo * should eventually have some sort of machine
20143afdf50SRichard Kuo * descriptor that has this stuff
20243afdf50SRichard Kuo */
20343afdf50SRichard Kuo
20443afdf50SRichard Kuo /* Right now, let's just fake it. */
20543afdf50SRichard Kuo for (i = 0; i < max_cpus; i++)
2060b5f9c00SRusty Russell set_cpu_present(i, true);
20743afdf50SRichard Kuo
20843afdf50SRichard Kuo /* Also need to register the interrupts for IPI */
20945b26ddeSafzal mohammed if (max_cpus > 1) {
21045b26ddeSafzal mohammed if (request_irq(irq, handle_ipi, IRQF_TRIGGER_RISING,
21145b26ddeSafzal mohammed "ipi_handler", NULL))
21245b26ddeSafzal mohammed pr_err("Failed to request irq %d (ipi_handler)\n", irq);
21345b26ddeSafzal mohammed }
21443afdf50SRichard Kuo }
21543afdf50SRichard Kuo
arch_smp_send_reschedule(int cpu)2164c8c3c7fSValentin Schneider void arch_smp_send_reschedule(int cpu)
21743afdf50SRichard Kuo {
21843afdf50SRichard Kuo send_ipi(cpumask_of(cpu), IPI_RESCHEDULE);
21943afdf50SRichard Kuo }
22043afdf50SRichard Kuo
smp_send_stop(void)22143afdf50SRichard Kuo void smp_send_stop(void)
22243afdf50SRichard Kuo {
22343afdf50SRichard Kuo struct cpumask targets;
22443afdf50SRichard Kuo cpumask_copy(&targets, cpu_online_mask);
22543afdf50SRichard Kuo cpumask_clear_cpu(smp_processor_id(), &targets);
22643afdf50SRichard Kuo send_ipi(&targets, IPI_CPU_STOP);
22743afdf50SRichard Kuo }
22843afdf50SRichard Kuo
arch_send_call_function_single_ipi(int cpu)22943afdf50SRichard Kuo void arch_send_call_function_single_ipi(int cpu)
23043afdf50SRichard Kuo {
231bd09f606SJiang Liu send_ipi(cpumask_of(cpu), IPI_CALL_FUNC);
23243afdf50SRichard Kuo }
23343afdf50SRichard Kuo
arch_send_call_function_ipi_mask(const struct cpumask * mask)23443afdf50SRichard Kuo void arch_send_call_function_ipi_mask(const struct cpumask *mask)
23543afdf50SRichard Kuo {
23643afdf50SRichard Kuo send_ipi(mask, IPI_CALL_FUNC);
23743afdf50SRichard Kuo }
23843afdf50SRichard Kuo
smp_start_cpus(void)23943afdf50SRichard Kuo void smp_start_cpus(void)
24043afdf50SRichard Kuo {
24143afdf50SRichard Kuo int i;
24243afdf50SRichard Kuo
24343afdf50SRichard Kuo for (i = 0; i < NR_CPUS; i++)
2440b5f9c00SRusty Russell set_cpu_possible(i, true);
24543afdf50SRichard Kuo }
246