1 /*
2  * SMP support for R-Mobile / SH-Mobile - r8a7779 portion
3  *
4  * Copyright (C) 2011  Renesas Solutions Corp.
5  * Copyright (C) 2011  Magnus Damm
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 #include <linux/kernel.h>
17 #include <linux/init.h>
18 #include <linux/smp.h>
19 #include <linux/spinlock.h>
20 #include <linux/io.h>
21 #include <linux/delay.h>
22 #include <linux/soc/renesas/rcar-sysc.h>
23 
24 #include <asm/cacheflush.h>
25 #include <asm/smp_plat.h>
26 #include <asm/smp_scu.h>
27 
28 #include "common.h"
29 #include "r8a7779.h"
30 
31 #define AVECR IOMEM(0xfe700040)
32 #define R8A7779_SCU_BASE 0xf0000000
33 
34 static const struct rcar_sysc_ch r8a7779_ch_cpu1 = {
35 	.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
36 	.chan_bit = 1, /* ARM1 */
37 	.isr_bit = 1, /* ARM1 */
38 };
39 
40 static const struct rcar_sysc_ch r8a7779_ch_cpu2 = {
41 	.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
42 	.chan_bit = 2, /* ARM2 */
43 	.isr_bit = 2, /* ARM2 */
44 };
45 
46 static const struct rcar_sysc_ch r8a7779_ch_cpu3 = {
47 	.chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
48 	.chan_bit = 3, /* ARM3 */
49 	.isr_bit = 3, /* ARM3 */
50 };
51 
52 static const struct rcar_sysc_ch * const r8a7779_ch_cpu[4] = {
53 	[1] = &r8a7779_ch_cpu1,
54 	[2] = &r8a7779_ch_cpu2,
55 	[3] = &r8a7779_ch_cpu3,
56 };
57 
58 static int r8a7779_platform_cpu_kill(unsigned int cpu)
59 {
60 	const struct rcar_sysc_ch *ch = NULL;
61 	int ret = -EIO;
62 
63 	cpu = cpu_logical_map(cpu);
64 
65 	if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))
66 		ch = r8a7779_ch_cpu[cpu];
67 
68 	if (ch)
69 		ret = rcar_sysc_power_down(ch);
70 
71 	return ret ? ret : 1;
72 }
73 
74 static int r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle)
75 {
76 	const struct rcar_sysc_ch *ch = NULL;
77 	unsigned int lcpu = cpu_logical_map(cpu);
78 	int ret;
79 
80 	if (lcpu < ARRAY_SIZE(r8a7779_ch_cpu))
81 		ch = r8a7779_ch_cpu[lcpu];
82 
83 	if (ch)
84 		ret = rcar_sysc_power_up(ch);
85 	else
86 		ret = -EIO;
87 
88 	return ret;
89 }
90 
91 static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus)
92 {
93 	/* Map the reset vector (in headsmp-scu.S, headsmp.S) */
94 	__raw_writel(__pa(shmobile_boot_vector), AVECR);
95 
96 	/* setup r8a7779 specific SCU bits */
97 	shmobile_smp_scu_prepare_cpus(R8A7779_SCU_BASE, max_cpus);
98 
99 	r8a7779_pm_init();
100 
101 	/* power off secondary CPUs */
102 	r8a7779_platform_cpu_kill(1);
103 	r8a7779_platform_cpu_kill(2);
104 	r8a7779_platform_cpu_kill(3);
105 }
106 
107 #ifdef CONFIG_HOTPLUG_CPU
108 static int r8a7779_cpu_kill(unsigned int cpu)
109 {
110 	if (shmobile_smp_scu_cpu_kill(cpu))
111 		return r8a7779_platform_cpu_kill(cpu);
112 
113 	return 0;
114 }
115 #endif /* CONFIG_HOTPLUG_CPU */
116 
117 const struct smp_operations r8a7779_smp_ops  __initconst = {
118 	.smp_prepare_cpus	= r8a7779_smp_prepare_cpus,
119 	.smp_boot_secondary	= r8a7779_boot_secondary,
120 #ifdef CONFIG_HOTPLUG_CPU
121 	.cpu_die		= shmobile_smp_scu_cpu_die,
122 	.cpu_kill		= r8a7779_cpu_kill,
123 #endif
124 };
125