2 * Spin Table SMP initialisation
4 * Copyright (C) 2013 ARM Ltd.
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.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <linux/delay.h>
20 #include <linux/init.h>
22 #include <linux/smp.h>
24 #include <asm/cacheflush.h>
25 #include <asm/cpu_ops.h>
26 #include <asm/cputype.h>
27 #include <asm/smp_plat.h>
29 extern void secondary_holding_pen(void);
30 volatile unsigned long secondary_holding_pen_release
= INVALID_HWID
;
33 #include <linux/of_address.h>
36 static phys_addr_t cpu_release_addr
[NR_CPUS
];
37 static DEFINE_RAW_SPINLOCK(boot_lock
);
40 * Write secondary_holding_pen_release in a way that is guaranteed to be
41 * visible to all observers, irrespective of whether they're taking part
42 * in coherency or not. This is necessary for the hotplug code to work
45 static void write_pen_release(u64 val
)
47 void *start
= (void *)&secondary_holding_pen_release
;
48 unsigned long size
= sizeof(secondary_holding_pen_release
);
50 secondary_holding_pen_release
= val
;
51 __flush_dcache_area(start
, size
);
55 static int smp_spin_table_cpu_init(struct device_node
*dn
, unsigned int cpu
)
58 * Determine the address from which the CPU is polling.
60 if (of_property_read_u64(dn
, "cpu-release-addr",
61 &cpu_release_addr
[cpu
])) {
62 pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
72 #define CCI400_SI4_BASE 0x5000
73 #define CCI400_SI4_SNOOP_CONTROL CCI400_SI4_BASE
74 #define DVM_MSG_REQ (1U << 1)
75 #define SNOOP_REQ (1U << 0)
76 #define CCI400_STATUS 0x000C
77 #define CHANGE_PENDING (1U << 0)
79 static int smp_spin_table_cpu_prepare(unsigned int cpu
)
83 struct device_node
*node
;
84 void __iomem
*cci400_base
;
86 if (!cpu_release_addr
[cpu
])
89 /*MTK only. Setup coherence interface*/
90 node
= of_find_compatible_node(NULL
, NULL
, "mediatek,CCI400");
93 cci400_base
= of_iomap(node
, 0);
95 printk(KERN_EMERG
"1.CCI400_SI4_SNOOP_CONTROL:0x%p, 0x%08x\n", cci400_base
+ CCI400_SI4_SNOOP_CONTROL
, readl(cci400_base
+ CCI400_SI4_SNOOP_CONTROL
));
96 /* Enable snoop requests and DVM message requests*/
97 writel(readl(cci400_base
+ CCI400_SI4_SNOOP_CONTROL
) | (SNOOP_REQ
| DVM_MSG_REQ
), cci400_base
+ CCI400_SI4_SNOOP_CONTROL
);
98 while (readl(cci400_base
+ CCI400_STATUS
) & CHANGE_PENDING
);
99 printk(KERN_EMERG
"2.CCI400_SI4_SNOOP_CONTROL:0x%p, 0x%08x\n", cci400_base
+ CCI400_SI4_SNOOP_CONTROL
,readl(cci400_base
+ CCI400_SI4_SNOOP_CONTROL
));
102 release_addr
= __va(cpu_release_addr
[cpu
]);
103 release_addr
[0] = (void *)__pa(secondary_holding_pen
);
104 __flush_dcache_area(release_addr
, sizeof(release_addr
[0]));
107 * Send an event to wake up the secondary CPU.
114 static int smp_spin_table_cpu_boot(unsigned int cpu
)
116 unsigned long timeout
;
119 * Set synchronisation state between this boot processor
120 * and the secondary one
122 raw_spin_lock(&boot_lock
);
125 * Update the pen release flag.
127 write_pen_release(cpu_logical_map(cpu
));
130 * Send an event, causing the secondaries to read pen_release.
134 timeout
= jiffies
+ (1 * HZ
);
135 while (time_before(jiffies
, timeout
)) {
136 if (secondary_holding_pen_release
== INVALID_HWID
)
142 * Now the secondary core is starting up let it run its
143 * calibrations, then wait for it to finish
145 raw_spin_unlock(&boot_lock
);
147 return secondary_holding_pen_release
!= INVALID_HWID
? -ENOSYS
: 0;
150 void smp_spin_table_cpu_postboot(void)
153 * Let the primary processor know we're out of the pen.
155 write_pen_release(INVALID_HWID
);
158 * Synchronise with the boot thread.
160 raw_spin_lock(&boot_lock
);
161 raw_spin_unlock(&boot_lock
);
164 const struct cpu_operations smp_spin_table_ops
= {
165 .name
= "spin-table",
166 .cpu_init
= smp_spin_table_cpu_init
,
167 .cpu_prepare
= smp_spin_table_cpu_prepare
,
168 .cpu_boot
= smp_spin_table_cpu_boot
,
169 .cpu_postboot
= smp_spin_table_cpu_postboot
,