oh, hello bluetooth dongle :D
[ppcskel.git] / ipc.c
1 /*
2         BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
3         Requires mini.
4
5 Copyright (C) 2008, 2009        Hector Martin "marcan" <marcan@marcansoft.com>
6 Copyright (C) 2009              Andre Heider "dhewg" <dhewg@wiibrew.org>
7 Copyright (C) 2009              John Kelley <wiidev@kelley.ca>
8
9 # This code is licensed to you under the terms of the GNU GPL, version 2;
10 # see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
11 */
12
13 #include "bootmii_ppc.h"
14 #include "ipc.h"
15 #include "string.h"
16 #include <stdarg.h>
17
18 static volatile ipc_request *in_queue;
19 static volatile ipc_request *out_queue;
20
21 static int in_size;
22 static int out_size;
23
24 static int initialized = 0;
25
26 static u16 out_head;
27 static u16 in_tail;
28
29 typedef const struct {
30         char magic[3];
31         char version;
32         void *mem2_boundary;
33         volatile ipc_request *ipc_in;
34         u32 ipc_in_size;
35         volatile ipc_request *ipc_out;
36         u32 ipc_out_size;
37 } ipc_infohdr;
38
39 static ipc_infohdr *infohdr;
40
41 static u32 cur_tag;
42
43 #define         HW_REG_BASE                     0xd000000
44
45 #define         HW_IPC_PPCMSG           (HW_REG_BASE + 0x000) //PPC to ARM
46 #define         HW_IPC_PPCCTRL          (HW_REG_BASE + 0x004)
47 #define         HW_IPC_ARMMSG           (HW_REG_BASE + 0x008) //ARM to PPC
48
49 #define         IPC_CTRL_SEND           0x01
50 // Set by peer to acknowledge a message. Write one to clear.
51 #define         IPC_CTRL_SENT           0x02
52 // Set by peer to send a message. Write one to clear.
53 #define         IPC_CTRL_RECV           0x04
54 // Write one acknowledge a message. Cleared when peer writes one to IPC_CTRL_SENT.
55 #define         IPC_CTRL_RECVD          0x08
56 // Enable interrupt when a message is received
57 #define         IPC_CTRL_INT_RECV       0x10
58 // Enable interrupt when a sent message is acknowledged
59 #define         IPC_CTRL_INT_SENT       0x20
60
61 static inline u16 peek_outtail(void)
62 {
63         return read32(HW_IPC_ARMMSG) & 0xFFFF;
64 }
65 static inline u16 peek_inhead(void)
66 {
67         return read32(HW_IPC_ARMMSG) >> 16;
68 }
69
70 static inline void poke_intail(u16 num)
71 {
72         mask32(HW_IPC_PPCMSG, 0xFFFF, num);
73 }
74 static inline void poke_outhead(u16 num)
75 {
76         mask32(HW_IPC_PPCMSG, 0xFFFF0000, num<<16);
77 }
78
79 int ipc_initialize(void)
80 {
81
82         infohdr = (ipc_infohdr*)(read32(0x13fffffc)|0x80000000);
83         sync_before_read((void*)infohdr, sizeof(ipc_infohdr));
84
85         printf("IPC: infoheader at %p %08x\n", infohdr);
86
87         if(memcmp(infohdr->magic, "IPC", 3)) {
88                 printf("IPC: bad magic on info structure\n",infohdr);
89                 return -1;
90         }
91         if(infohdr->version != 1) {
92                 printf("IPC: unknown IPC version %d\n",infohdr->version);
93                 return -1;
94         }
95
96         in_queue = (void*)(((u32)infohdr->ipc_in)|0x80000000);
97         out_queue = (void*)(((u32)infohdr->ipc_out)|0x80000000);
98
99         in_size = infohdr->ipc_in_size;
100         out_size = infohdr->ipc_out_size;
101
102         in_tail = read32(HW_IPC_PPCMSG) & 0xffff;
103         out_head = read32(HW_IPC_PPCMSG) >> 16;
104
105         printf("IPC: initial in tail: %d, out head: %d\n", in_tail, out_head);
106
107         cur_tag = 1;
108
109         initialized = 1;
110         return 0;
111 }
112
113 void ipc_shutdown(void)
114 {
115         if(!initialized)
116                 return;
117         ipc_flush();
118         initialized = 0;
119 }
120
121 void ipc_vpost(u32 code, u32 tag, u32 num_args, va_list ap)
122 {
123         int arg = 0;
124         int n = 0;
125
126         if(!initialized) {
127                 printf("IPC: not inited\n");
128                 return;
129         }
130
131         if(peek_inhead() == ((in_tail + 1)&(in_size-1))) {
132                 printf("IPC: in queue full, spinning\n");
133                 while(peek_inhead() == ((in_tail + 1)&(in_size-1))) {
134                         udelay(10);
135                         if(n++ > 20000) {
136                                 printf("IPC: ARM might be stuck, still waiting for inhead %d != %d\n",
137                                         peek_inhead(), ((in_tail + 1)&(in_size-1)));
138                                 n = 0;
139                         }
140                 }
141         }
142         in_queue[in_tail].code = code;
143         in_queue[in_tail].tag = tag;
144         while(num_args--) {
145                 in_queue[in_tail].args[arg++] = va_arg(ap, u32);
146         }
147         sync_after_write((void*)&in_queue[in_tail], 32);
148         in_tail = (in_tail+1)&(in_size-1);
149         poke_intail(in_tail);
150         write32(HW_IPC_PPCCTRL, IPC_CTRL_SEND);
151 }
152
153 void ipc_post(u32 code, u32 tag, u32 num_args, ...)
154 {
155         va_list ap;
156
157         if(num_args)
158                 va_start(ap, num_args);
159
160         ipc_vpost(code, tag, num_args, ap);
161
162         if(num_args)
163                 va_end(ap);
164 }
165
166 void ipc_flush(void)
167 {
168         int n = 0;
169         if(!initialized) {
170                 printf("IPC: not inited\n");
171                 return;
172         }
173         while(peek_inhead() != in_tail) {
174                 udelay(10);
175                 if(n++ > 20000) {
176                         printf("IPC: ARM might be stuck, still waiting for inhead %d == intail %d\n",
177                                 peek_inhead(), in_tail);
178                         n = 0;
179                 }
180         }
181 }
182
183 // last IPC message received, copied because we need to make space in the queue
184 ipc_request req_recv;
185
186 // since we're not using IRQs, we don't use the reception bell at all at the moment
187 ipc_request *ipc_receive(void)
188 {
189         while(peek_outtail() == out_head);
190         sync_before_read((void*)&out_queue[out_head], 32);
191         req_recv = out_queue[out_head];
192         out_head = (out_head+1)&(out_size-1);
193         poke_outhead(out_head);
194
195         return &req_recv;
196 }
197
198 void ipc_process_unhandled(volatile ipc_request *rep)
199 {
200         printf("IPC: Unhandled message: %08x %08x [%08x %08x %08x %08x %08x %08x]\n",
201                 rep->code, rep->tag, rep->args[0], rep->args[1], rep->args[2], rep->args[3], 
202                 rep->args[4], rep->args[5]);
203 }
204
205 ipc_request *ipc_receive_tagged(u32 code, u32 tag)
206 {
207         ipc_request *rep;
208         rep = ipc_receive();
209         while(rep->code != code || rep->tag != tag) {
210                 ipc_process_unhandled(rep);
211                 rep = ipc_receive();
212         }
213         return rep;
214 }
215
216 ipc_request *ipc_exchange(u32 code, u32 num_args, ...)
217 {
218         va_list ap;
219         ipc_request *rep;
220
221         if(num_args)
222                 va_start(ap, num_args);
223
224         ipc_vpost(code, cur_tag, num_args, ap);
225
226         if(num_args)
227                 va_end(ap);
228
229         rep = ipc_receive_tagged(code, cur_tag);
230
231         cur_tag++;
232         return rep;
233 }
234