1/*
2 *  PS3 SMP routines.
3 *
4 *  Copyright (C) 2006 Sony Computer Entertainment Inc.
5 *  Copyright 2006 Sony Corp.
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 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21#include <linux/kernel.h>
22#include <linux/smp.h>
23
24#include <asm/machdep.h>
25#include <asm/udbg.h>
26
27#include "platform.h"
28
29#if defined(DEBUG)
30#define DBG udbg_printf
31#else
32#define DBG pr_debug
33#endif
34
35/**
36  * ps3_ipi_virqs - a per cpu array of virqs for ipi use
37  */
38
39#define MSG_COUNT 4
40static DEFINE_PER_CPU(unsigned int [MSG_COUNT], ps3_ipi_virqs);
41
42static void ps3_smp_message_pass(int cpu, int msg)
43{
44	int result;
45	unsigned int virq;
46
47	if (msg >= MSG_COUNT) {
48		DBG("%s:%d: bad msg: %d\n", __func__, __LINE__, msg);
49		return;
50	}
51
52	virq = per_cpu(ps3_ipi_virqs, cpu)[msg];
53	result = ps3_send_event_locally(virq);
54
55	if (result)
56		DBG("%s:%d: ps3_send_event_locally(%d, %d) failed"
57			" (%d)\n", __func__, __LINE__, cpu, msg, result);
58}
59
60static int __init ps3_smp_probe(void)
61{
62	int cpu;
63
64	for (cpu = 0; cpu < 2; cpu++) {
65		int result;
66		unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
67		int i;
68
69		DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
70
71		/*
72		* Check assumptions on ps3_ipi_virqs[] indexing. If this
73		* check fails, then a different mapping of PPC_MSG_
74		* to index needs to be setup.
75		*/
76
77		BUILD_BUG_ON(PPC_MSG_CALL_FUNCTION    != 0);
78		BUILD_BUG_ON(PPC_MSG_RESCHEDULE       != 1);
79		BUILD_BUG_ON(PPC_MSG_CALL_FUNC_SINGLE != 2);
80		BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK   != 3);
81
82		for (i = 0; i < MSG_COUNT; i++) {
83			result = ps3_event_receive_port_setup(cpu, &virqs[i]);
84
85			if (result)
86				continue;
87
88			DBG("%s:%d: (%d, %d) => virq %u\n",
89				__func__, __LINE__, cpu, i, virqs[i]);
90
91			result = smp_request_message_ipi(virqs[i], i);
92
93			if (result)
94				virqs[i] = NO_IRQ;
95			else
96				ps3_register_ipi_irq(cpu, virqs[i]);
97		}
98
99		ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]);
100
101		DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
102	}
103
104	return 2;
105}
106
107void ps3_smp_cleanup_cpu(int cpu)
108{
109	unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
110	int i;
111
112	DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
113
114	for (i = 0; i < MSG_COUNT; i++) {
115		/* Can't call free_irq from interrupt context. */
116		ps3_event_receive_port_destroy(virqs[i]);
117		virqs[i] = NO_IRQ;
118	}
119
120	DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
121}
122
123static struct smp_ops_t ps3_smp_ops = {
124	.probe		= ps3_smp_probe,
125	.message_pass	= ps3_smp_message_pass,
126	.kick_cpu	= smp_generic_kick_cpu,
127};
128
129void smp_init_ps3(void)
130{
131	DBG(" -> %s\n", __func__);
132	smp_ops = &ps3_smp_ops;
133	DBG(" <- %s\n", __func__);
134}
135