1/*
2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include <arch_platform.h>
7
8#include <new>
9
10#include <KernelExport.h>
11
12#include <arch/generic/debug_uart.h>
13#include <boot/kernel_args.h>
14#include <platform/openfirmware/openfirmware.h>
15#include <real_time_clock.h>
16#include <util/kernel_cpp.h>
17
18
19void *gFDT;
20static PPCPlatform *sPPCPlatform;
21
22
23PPCPlatform::PPCPlatform(ppc_platform_type platformType)
24	: fPlatformType(platformType)
25{
26}
27
28
29PPCPlatform::~PPCPlatform()
30{
31}
32
33
34PPCPlatform *
35PPCPlatform::Default()
36{
37	return sPPCPlatform;
38}
39
40
41// #pragma mark - Open Firmware
42
43
44namespace BPrivate {
45
46class PPCOpenFirmware : public PPCPlatform {
47public:
48	PPCOpenFirmware();
49	virtual ~PPCOpenFirmware();
50
51	virtual status_t Init(struct kernel_args *kernelArgs);
52	virtual status_t InitSerialDebug(struct kernel_args *kernelArgs);
53	virtual status_t InitPostVM(struct kernel_args *kernelArgs);
54	virtual status_t InitRTC(struct kernel_args *kernelArgs,
55		struct real_time_data *data);
56
57	virtual char SerialDebugGetChar();
58	virtual void SerialDebugPutChar(char c);
59
60	virtual	void SetHardwareRTC(uint32 seconds);
61	virtual	uint32 GetHardwareRTC();
62
63	virtual	void ShutDown(bool reboot);
64
65private:
66	int	fInput;
67	int	fOutput;
68	int	fRTC;
69};
70
71}	// namespace BPrivate
72
73
74using BPrivate::PPCOpenFirmware;
75
76
77// OF debugger commands
78
79
80static int
81debug_command_of_exit(int argc, char **argv)
82{
83	of_exit();
84	kprintf("of_exit() failed!\n");
85	return 0;
86}
87
88
89static int
90debug_command_of_enter(int argc, char **argv)
91{
92	of_call_client_function("enter", 0, 0);
93	return 0;
94}
95
96
97PPCOpenFirmware::PPCOpenFirmware()
98	: PPCPlatform(PPC_PLATFORM_OPEN_FIRMWARE),
99	  fInput(-1),
100	  fOutput(-1),
101	  fRTC(-1)
102{
103}
104
105
106PPCOpenFirmware::~PPCOpenFirmware()
107{
108}
109
110
111status_t
112PPCOpenFirmware::Init(struct kernel_args *kernelArgs)
113{
114	return of_init(
115		(intptr_t(*)(void*))kernelArgs->platform_args.openfirmware_entry);
116}
117
118
119status_t
120PPCOpenFirmware::InitSerialDebug(struct kernel_args *kernelArgs)
121{
122	if (of_getprop(gChosen, "stdin", &fInput, sizeof(int)) == OF_FAILED)
123		return B_ERROR;
124	if (!kernelArgs->frame_buffer.enabled) {
125		if (of_getprop(gChosen, "stdout", &fOutput, sizeof(int)) == OF_FAILED)
126			return B_ERROR;
127	}
128
129	return B_OK;
130}
131
132
133status_t
134PPCOpenFirmware::InitPostVM(struct kernel_args *kernelArgs)
135{
136	add_debugger_command("of_exit", &debug_command_of_exit,
137		"Exit to the Open Firmware prompt. No way to get back into the OS!");
138	add_debugger_command("of_enter", &debug_command_of_enter,
139		"Enter a subordinate Open Firmware interpreter. Quitting it returns "
140		"to KDL.");
141
142	return B_OK;
143}
144
145
146// InitRTC
147status_t
148PPCOpenFirmware::InitRTC(struct kernel_args *kernelArgs,
149	struct real_time_data *data)
150{
151	// open RTC
152	fRTC = of_open(kernelArgs->platform_args.rtc_path);
153	if (fRTC == OF_FAILED) {
154		dprintf("PPCOpenFirmware::InitRTC(): Failed open RTC device!\n");
155		return B_ERROR;
156	}
157
158	return B_OK;
159}
160
161
162char
163PPCOpenFirmware::SerialDebugGetChar()
164{
165	int key;
166	if (of_interpret("key", 0, 1, &key) == OF_FAILED)
167		return 0;
168	return (char)key;
169}
170
171
172void
173PPCOpenFirmware::SerialDebugPutChar(char c)
174{
175	if (fOutput == -1)
176		return;
177
178	if (c == '\n')
179		of_write(fOutput, "\r\n", 2);
180	else
181		of_write(fOutput, &c, 1);
182}
183
184
185void
186PPCOpenFirmware::SetHardwareRTC(uint32 seconds)
187{
188	struct tm t;
189	rtc_secs_to_tm(seconds, &t);
190
191	t.tm_year += RTC_EPOCH_BASE_YEAR;
192	t.tm_mon++;
193
194	if (of_call_method(fRTC, "set-time", 6, 0, t.tm_year, t.tm_mon, t.tm_mday,
195			t.tm_hour, t.tm_min, t.tm_sec) == OF_FAILED) {
196		dprintf("PPCOpenFirmware::SetHardwareRTC(): Failed to set RTC!\n");
197	}
198}
199
200
201uint32
202PPCOpenFirmware::GetHardwareRTC()
203{
204	struct tm t;
205	if (of_call_method(fRTC, "get-time", 0, 6, &t.tm_year, &t.tm_mon,
206			&t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) == OF_FAILED) {
207		dprintf("PPCOpenFirmware::GetHardwareRTC(): Failed to get RTC!\n");
208		return 0;
209	}
210
211	t.tm_year -= RTC_EPOCH_BASE_YEAR;
212	t.tm_mon--;
213
214	return rtc_tm_to_secs(&t);
215}
216
217
218void
219PPCOpenFirmware::ShutDown(bool reboot)
220{
221	if (reboot) {
222		of_interpret("reset-all", 0, 0);
223	} else {
224		// not standardized, so it might fail
225		of_interpret("shut-down", 0, 0);
226	}
227}
228
229
230// #pragma mark - U-Boot + FDT
231
232
233namespace BPrivate {
234
235class PPCUBoot : public PPCPlatform {
236public:
237	PPCUBoot();
238	virtual ~PPCUBoot();
239
240	virtual status_t Init(struct kernel_args *kernelArgs);
241	virtual status_t InitSerialDebug(struct kernel_args *kernelArgs);
242	virtual status_t InitPostVM(struct kernel_args *kernelArgs);
243	virtual status_t InitRTC(struct kernel_args *kernelArgs,
244		struct real_time_data *data);
245
246	virtual char SerialDebugGetChar();
247	virtual void SerialDebugPutChar(char c);
248
249	virtual	void SetHardwareRTC(uint32 seconds);
250	virtual	uint32 GetHardwareRTC();
251
252	virtual	void ShutDown(bool reboot);
253
254private:
255	int	fInput;
256	int	fOutput;
257	int	fRTC;
258	DebugUART *fDebugUART;
259};
260
261}	// namespace BPrivate
262
263using BPrivate::PPCUBoot;
264
265
266PPCUBoot::PPCUBoot()
267	: PPCPlatform(PPC_PLATFORM_U_BOOT),
268	  fInput(-1),
269	  fOutput(-1),
270	  fRTC(-1),
271	  fDebugUART(NULL)
272{
273}
274
275
276PPCUBoot::~PPCUBoot()
277{
278}
279
280
281status_t
282PPCUBoot::Init(struct kernel_args *kernelArgs)
283{
284	gFDT = kernelArgs->platform_args.fdt;
285	// XXX: do we error out if no FDT?
286	return B_OK;
287}
288
289
290status_t
291PPCUBoot::InitSerialDebug(struct kernel_args *kernelArgs)
292{
293	// TODO: get relevant debug uart from fdt
294	//fDebugUART = debug_uart_from_fdt(gFDT);
295	if (fDebugUART == NULL)
296		return B_ERROR;
297	return B_OK;
298}
299
300
301status_t
302PPCUBoot::InitPostVM(struct kernel_args *kernelArgs)
303{
304	return B_ERROR;
305}
306
307
308status_t
309PPCUBoot::InitRTC(struct kernel_args *kernelArgs,
310	struct real_time_data *data)
311{
312	return B_ERROR;
313}
314
315
316char
317PPCUBoot::SerialDebugGetChar()
318{
319	if (fDebugUART)
320		return fDebugUART->GetChar(false);
321	return 0;
322}
323
324
325void
326PPCUBoot::SerialDebugPutChar(char c)
327{
328	if (fDebugUART)
329		fDebugUART->PutChar(c);
330}
331
332
333void
334PPCUBoot::SetHardwareRTC(uint32 seconds)
335{
336}
337
338
339uint32
340PPCUBoot::GetHardwareRTC()
341{
342	return 0;
343}
344
345
346void
347PPCUBoot::ShutDown(bool reboot)
348{
349}
350
351
352// # pragma mark -
353
354
355#define PLATFORM_BUFFER_SIZE MAX(sizeof(PPCOpenFirmware),sizeof(PPCUBoot))
356// static buffer for constructing the actual PPCPlatform
357static char *sPPCPlatformBuffer[PLATFORM_BUFFER_SIZE];
358
359
360status_t
361arch_platform_init(struct kernel_args *kernelArgs)
362{
363	// only OpenFirmware supported for now
364	switch (kernelArgs->arch_args.platform) {
365		case PPC_PLATFORM_OPEN_FIRMWARE:
366			sPPCPlatform = new(sPPCPlatformBuffer) PPCOpenFirmware;
367			break;
368		case PPC_PLATFORM_U_BOOT:
369			sPPCPlatform = new(sPPCPlatformBuffer) PPCUBoot;
370			break;
371		default:
372			return B_ERROR;
373	}
374
375	return sPPCPlatform->Init(kernelArgs);
376}
377
378
379status_t
380arch_platform_init_post_vm(struct kernel_args *kernelArgs)
381{
382	return sPPCPlatform->InitPostVM(kernelArgs);
383}
384
385
386status_t
387arch_platform_init_post_thread(struct kernel_args *kernelArgs)
388{
389	return B_OK;
390}
391