ARCv2: SMP: Support ARConnect (MCIP) for Inter-Core-Interrupts et al
[firefly-linux-kernel-4.4.55.git] / arch / arc / kernel / mcip.c
1 /*
2  * ARC ARConnect (MultiCore IP) support (formerly known as MCIP)
3  *
4  * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/smp.h>
12 #include <linux/irq.h>
13 #include <linux/spinlock.h>
14 #include <asm/mcip.h>
15
16 static char smp_cpuinfo_buf[128];
17
18 static DEFINE_RAW_SPINLOCK(mcip_lock);
19
20
21 /*
22  * Any SMP specific init any CPU does when it comes up.
23  * Here we setup the CPU to enable Inter-Processor-Interrupts
24  * Called for each CPU
25  * -Master      : init_IRQ()
26  * -Other(s)    : start_kernel_secondary()
27  */
28 void mcip_init_smp(unsigned int cpu)
29 {
30         smp_ipi_irq_setup(cpu, IPI_IRQ);
31 }
32
33 static void mcip_ipi_send(int cpu)
34 {
35         unsigned long flags;
36
37         raw_spin_lock_irqsave(&mcip_lock, flags);
38         __mcip_cmd(CMD_INTRPT_GENERATE_IRQ, cpu);
39         raw_spin_unlock_irqrestore(&mcip_lock, flags);
40 }
41
42 static void mcip_ipi_clear(int irq)
43 {
44         unsigned int cpu;
45         unsigned long flags;
46
47         raw_spin_lock_irqsave(&mcip_lock, flags);
48
49         /* Who sent the IPI */
50         __mcip_cmd(CMD_INTRPT_CHECK_SOURCE, 0);
51
52         cpu = read_aux_reg(ARC_REG_MCIP_READBACK);      /* 1,2,4,8... */
53
54         __mcip_cmd(CMD_INTRPT_GENERATE_ACK, __ffs(cpu)); /* 0,1,2,3... */
55
56         raw_spin_unlock_irqrestore(&mcip_lock, flags);
57 }
58
59 volatile int wake_flag;
60
61 static void mcip_wakeup_cpu(int cpu, unsigned long pc)
62 {
63         BUG_ON(cpu == 0);
64         wake_flag = cpu;
65 }
66
67 void arc_platform_smp_wait_to_boot(int cpu)
68 {
69         while (wake_flag != cpu)
70                 ;
71
72         wake_flag = 0;
73         __asm__ __volatile__("j @first_lines_of_secondary       \n");
74 }
75
76 struct plat_smp_ops plat_smp_ops = {
77         .info           = smp_cpuinfo_buf,
78         .cpu_kick       = mcip_wakeup_cpu,
79         .ipi_send       = mcip_ipi_send,
80         .ipi_clear      = mcip_ipi_clear,
81 };
82
83 void mcip_init_early_smp(void)
84 {
85 #define IS_AVAIL1(var, str)    ((var) ? str : "")
86
87         struct mcip_bcr {
88 #ifdef CONFIG_CPU_BIG_ENDIAN
89                 unsigned int pad3:8,
90                              idu:1, llm:1, num_cores:6,
91                              iocoh:1,  grtc:1, dbg:1, pad2:1,
92                              msg:1, sem:1, ipi:1, pad:1,
93                              ver:8;
94 #else
95                 unsigned int ver:8,
96                              pad:1, ipi:1, sem:1, msg:1,
97                              pad2:1, dbg:1, grtc:1, iocoh:1,
98                              num_cores:6, llm:1, idu:1,
99                              pad3:8;
100 #endif
101         } mp;
102
103         READ_BCR(ARC_REG_MCIP_BCR, mp);
104
105         sprintf(smp_cpuinfo_buf,
106                 "Extn [SMP]\t: ARConnect (v%d): %d cores with %s%s%s%s\n",
107                 mp.ver, mp.num_cores,
108                 IS_AVAIL1(mp.ipi, "IPI "),
109                 IS_AVAIL1(mp.idu, "IDU "),
110                 IS_AVAIL1(mp.dbg, "DEBUG "),
111                 IS_AVAIL1(mp.grtc, "GRTC"));
112
113         if (mp.dbg) {
114                 __mcip_cmd_data(CMD_DEBUG_SET_SELECT, 0, 0xf);
115                 __mcip_cmd_data(CMD_DEBUG_SET_MASK, 0xf, 0xf);
116         }
117 }