source: mainline/kernel/arch/arm32/src/drivers/gxemul.c@ 516ff92

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 516ff92 was 516ff92, checked in by Martin Decky <martin@…>, 17 years ago

silent kernel console output when user space console is active

  • Property mode set to 100644
File size: 9.5 KB
Line 
1/*
2 * Copyright (c) 2007 Michal Kebrt, Petr Stepan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup arm32gxemul
30 * @{
31 */
32/** @file
33 * @brief GXemul drivers.
34 */
35
36#include <interrupt.h>
37#include <ipc/irq.h>
38#include <console/chardev.h>
39#include <arch/drivers/gxemul.h>
40#include <console/console.h>
41#include <sysinfo/sysinfo.h>
42#include <print.h>
43#include <ddi/device.h>
44#include <mm/page.h>
45#include <arch/machine.h>
46#include <arch/debug/print.h>
47
48/* Addresses of devices. */
49#define GXEMUL_VIDEORAM 0x10000000
50#define GXEMUL_KBD 0x10000000
51#define GXEMUL_HALT_OFFSET 0x10
52#define GXEMUL_RTC 0x15000000
53#define GXEMUL_RTC_FREQ_OFFSET 0x100
54#define GXEMUL_RTC_ACK_OFFSET 0x110
55#define GXEMUL_IRQC 0x16000000
56#define GXEMUL_IRQC_MASK_OFFSET 0x4
57#define GXEMUL_IRQC_UNMASK_OFFSET 0x8
58#define GXEMUL_MP 0x11000000
59#define GXEMUL_MP_MEMSIZE_OFFSET 0x0090
60#define GXEMUL_FB 0x12000000
61
62/* IRQs */
63#define GXEMUL_KBD_IRQ 2
64#define GXEMUL_TIMER_IRQ 4
65
66static gxemul_hw_map_t gxemul_hw_map;
67static chardev_t console;
68static irq_t gxemul_console_irq;
69static irq_t gxemul_timer_irq;
70
71static bool hw_map_init_called = false;
72
73static void gxemul_kbd_enable(chardev_t *dev);
74static void gxemul_kbd_disable(chardev_t *dev);
75static void gxemul_write(chardev_t *dev, const char ch);
76static char gxemul_do_read(chardev_t *dev);
77
78static chardev_operations_t gxemul_ops = {
79 .resume = gxemul_kbd_enable,
80 .suspend = gxemul_kbd_disable,
81 .write = gxemul_write,
82 .read = gxemul_do_read,
83};
84
85
86/** Returns the mask of active interrupts. */
87static inline uint32_t gxemul_irqc_get_sources(void)
88{
89 return *((uint32_t *) gxemul_hw_map.irqc);
90}
91
92
93/** Masks interrupt.
94 *
95 * @param irq interrupt number
96 */
97static inline void gxemul_irqc_mask(uint32_t irq)
98{
99 *((uint32_t *) gxemul_hw_map.irqc_mask) = irq;
100}
101
102
103/** Unmasks interrupt.
104 *
105 * @param irq interrupt number
106 */
107static inline void gxemul_irqc_unmask(uint32_t irq)
108{
109 *((uint32_t *) gxemul_hw_map.irqc_unmask) = irq;
110}
111
112
113/** Initializes #gxemul_hw_map. */
114void gxemul_hw_map_init(void)
115{
116 gxemul_hw_map.videoram = hw_map(GXEMUL_VIDEORAM, PAGE_SIZE);
117 gxemul_hw_map.kbd = hw_map(GXEMUL_KBD, PAGE_SIZE);
118 gxemul_hw_map.rtc = hw_map(GXEMUL_RTC, PAGE_SIZE);
119 gxemul_hw_map.irqc = hw_map(GXEMUL_IRQC, PAGE_SIZE);
120
121 gxemul_hw_map.rtc_freq = gxemul_hw_map.rtc + GXEMUL_RTC_FREQ_OFFSET;
122 gxemul_hw_map.rtc_ack = gxemul_hw_map.rtc + GXEMUL_RTC_ACK_OFFSET;
123 gxemul_hw_map.irqc_mask = gxemul_hw_map.irqc + GXEMUL_IRQC_MASK_OFFSET;
124 gxemul_hw_map.irqc_unmask = gxemul_hw_map.irqc +
125 GXEMUL_IRQC_UNMASK_OFFSET;
126
127 hw_map_init_called = true;
128}
129
130
131/** Putchar that works with gxemul.
132 *
133 * @param dev Not used.
134 * @param ch Characted to be printed.
135 */
136static void gxemul_write(chardev_t *dev, const char ch, bool silent)
137{
138 if (!silent)
139 *((char *) gxemul_hw_map.videoram) = ch;
140}
141
142/** Enables gxemul keyboard (interrupt unmasked).
143 *
144 * @param dev Not used.
145 *
146 * Called from getc().
147 */
148static void gxemul_kbd_enable(chardev_t *dev)
149{
150 gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
151}
152
153/** Disables gxemul keyboard (interrupt masked).
154 *
155 * @param dev not used
156 *
157 * Called from getc().
158 */
159static void gxemul_kbd_disable(chardev_t *dev)
160{
161 gxemul_irqc_mask(GXEMUL_KBD_IRQ);
162}
163
164/** Read character using polling, assume interrupts disabled.
165 *
166 * @param dev Not used.
167 */
168static char gxemul_do_read(chardev_t *dev)
169{
170 char ch;
171
172 while (1) {
173 ch = *((volatile char *) gxemul_hw_map.kbd);
174 if (ch) {
175 if (ch == '\r')
176 return '\n';
177 if (ch == 0x7f)
178 return '\b';
179 return ch;
180 }
181 }
182}
183
184/** Process keyboard interrupt.
185 *
186 * @param irq IRQ information.
187 * @param arg Not used.
188 */
189static void gxemul_irq_handler(irq_t *irq, void *arg, ...)
190{
191 if ((irq->notif_cfg.notify) && (irq->notif_cfg.answerbox)) {
192 ipc_irq_send_notif(irq);
193 } else {
194 char ch = 0;
195
196 ch = *((char *) gxemul_hw_map.kbd);
197 if (ch == '\r') {
198 ch = '\n';
199 }
200 if (ch == 0x7f) {
201 ch = '\b';
202 }
203 chardev_push_character(&console, ch);
204 }
205}
206
207static irq_ownership_t gxemul_claim(void)
208{
209 return IRQ_ACCEPT;
210}
211
212
213/** Acquire console back for kernel. */
214void gxemul_grab_console(void)
215{
216 ipl_t ipl = interrupts_disable();
217 spinlock_lock(&gxemul_console_irq.lock);
218 gxemul_console_irq.notif_cfg.notify = false;
219 spinlock_unlock(&gxemul_console_irq.lock);
220 interrupts_restore(ipl);
221}
222
223/** Return console to userspace. */
224void gxemul_release_console(void)
225{
226 ipl_t ipl = interrupts_disable();
227 spinlock_lock(&gxemul_console_irq.lock);
228 if (gxemul_console_irq.notif_cfg.answerbox) {
229 gxemul_console_irq.notif_cfg.notify = true;
230 }
231 spinlock_unlock(&gxemul_console_irq.lock);
232 interrupts_restore(ipl);
233}
234
235/** Initializes console object representing gxemul console.
236 *
237 * @param devno device number.
238 */
239void gxemul_console_init(devno_t devno)
240{
241 chardev_initialize("gxemul_console", &console, &gxemul_ops);
242 stdin = &console;
243 stdout = &console;
244
245 irq_initialize(&gxemul_console_irq);
246 gxemul_console_irq.devno = devno;
247 gxemul_console_irq.inr = GXEMUL_KBD_IRQ;
248 gxemul_console_irq.claim = gxemul_claim;
249 gxemul_console_irq.handler = gxemul_irq_handler;
250 irq_register(&gxemul_console_irq);
251
252 gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
253
254 sysinfo_set_item_val("kbd", NULL, true);
255 sysinfo_set_item_val("kbd.devno", NULL, devno);
256 sysinfo_set_item_val("kbd.inr", NULL, GXEMUL_KBD_IRQ);
257 sysinfo_set_item_val("kbd.address.virtual", NULL, gxemul_hw_map.kbd);
258}
259
260/** Starts gxemul Real Time Clock device, which asserts regular interrupts.
261 *
262 * @param frequency Interrupts frequency (0 disables RTC).
263 */
264static void gxemul_timer_start(uint32_t frequency)
265{
266 *((uint32_t*) gxemul_hw_map.rtc_freq) = frequency;
267}
268
269static irq_ownership_t gxemul_timer_claim(void)
270{
271 return IRQ_ACCEPT;
272}
273
274/** Timer interrupt handler.
275 *
276 * @param irq Interrupt information.
277 * @param arg Not used.
278 */
279static void gxemul_timer_irq_handler(irq_t *irq, void *arg, ...)
280{
281 /*
282 * We are holding a lock which prevents preemption.
283 * Release the lock, call clock() and reacquire the lock again.
284 */
285 spinlock_unlock(&irq->lock);
286 clock();
287 spinlock_lock(&irq->lock);
288
289 /* acknowledge tick */
290 *((uint32_t*) gxemul_hw_map.rtc_ack) = 0;
291}
292
293/** Initializes and registers timer interrupt handler. */
294static void gxemul_timer_irq_init(void)
295{
296 irq_initialize(&gxemul_timer_irq);
297 gxemul_timer_irq.devno = device_assign_devno();
298 gxemul_timer_irq.inr = GXEMUL_TIMER_IRQ;
299 gxemul_timer_irq.claim = gxemul_timer_claim;
300 gxemul_timer_irq.handler = gxemul_timer_irq_handler;
301
302 irq_register(&gxemul_timer_irq);
303}
304
305
306/** Starts timer.
307 *
308 * Initiates regular timer interrupts after initializing
309 * corresponding interrupt handler.
310 */
311void gxemul_timer_irq_start(void)
312{
313 gxemul_timer_irq_init();
314 gxemul_timer_start(GXEMUL_TIMER_FREQ);
315}
316
317/** Returns the size of emulated memory.
318 *
319 * @return Size in bytes.
320 */
321size_t gxemul_get_memory_size(void)
322{
323 return *((int *) (GXEMUL_MP + GXEMUL_MP_MEMSIZE_OFFSET));
324}
325
326/** Prints a character.
327 *
328 * @param ch Character to be printed.
329 */
330void gxemul_debug_putc(char ch)
331{
332 char *addr = 0;
333 if (!hw_map_init_called) {
334 addr = (char *) GXEMUL_KBD;
335 } else {
336 addr = (char *) gxemul_hw_map.videoram;
337 }
338
339 *(addr) = ch;
340}
341
342/** Stops gxemul. */
343void gxemul_cpu_halt(void)
344{
345 char * addr = 0;
346 if (!hw_map_init_called) {
347 addr = (char *) GXEMUL_KBD;
348 } else {
349 addr = (char *) gxemul_hw_map.videoram;
350 }
351
352 *(addr + GXEMUL_HALT_OFFSET) = '\0';
353}
354
355/** Gxemul specific interrupt exception handler.
356 *
357 * Determines sources of the interrupt from interrupt controller and
358 * calls high-level handlers for them.
359 *
360 * @param exc_no Interrupt exception number.
361 * @param istate Saved processor state.
362 */
363void gxemul_irq_exception(int exc_no, istate_t *istate)
364{
365 uint32_t sources = gxemul_irqc_get_sources();
366 int i;
367
368 for (i = 0; i < GXEMUL_IRQC_MAX_IRQ; i++) {
369 if (sources & (1 << i)) {
370 irq_t *irq = irq_dispatch_and_lock(i);
371 if (irq) {
372 /* The IRQ handler was found. */
373 irq->handler(irq, irq->arg);
374 spinlock_unlock(&irq->lock);
375 } else {
376 /* Spurious interrupt.*/
377 dprintf("cpu%d: spurious interrupt (inum=%d)\n",
378 CPU->id, i);
379 }
380 }
381 }
382}
383
384/** Returns address of framebuffer device.
385 *
386 * @return Address of framebuffer device.
387 */
388uintptr_t gxemul_get_fb_address(void)
389{
390 return (uintptr_t) GXEMUL_FB;
391}
392
393/** @}
394 */
Note: See TracBrowser for help on using the repository browser.