source: mainline/kernel/arch/arm32/src/drivers/gxemul.c@ 308cdd1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 308cdd1 was 6b781c0, checked in by Jakub Jermar <jakub@…>, 18 years ago

Merge arm32 into trunk.

  • 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)
137{
138 *((char *) gxemul_hw_map.videoram) = ch;
139}
140
141/** Enables gxemul keyboard (interrupt unmasked).
142 *
143 * @param dev Not used.
144 *
145 * Called from getc().
146 */
147static void gxemul_kbd_enable(chardev_t *dev)
148{
149 gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
150}
151
152/** Disables gxemul keyboard (interrupt masked).
153 *
154 * @param dev not used
155 *
156 * Called from getc().
157 */
158static void gxemul_kbd_disable(chardev_t *dev)
159{
160 gxemul_irqc_mask(GXEMUL_KBD_IRQ);
161}
162
163/** Read character using polling, assume interrupts disabled.
164 *
165 * @param dev Not used.
166 */
167static char gxemul_do_read(chardev_t *dev)
168{
169 char ch;
170
171 while (1) {
172 ch = *((volatile char *) gxemul_hw_map.kbd);
173 if (ch) {
174 if (ch == '\r')
175 return '\n';
176 if (ch == 0x7f)
177 return '\b';
178 return ch;
179 }
180 }
181}
182
183/** Process keyboard interrupt.
184 *
185 * @param irq IRQ information.
186 * @param arg Not used.
187 */
188static void gxemul_irq_handler(irq_t *irq, void *arg, ...)
189{
190 if ((irq->notif_cfg.notify) && (irq->notif_cfg.answerbox)) {
191 ipc_irq_send_notif(irq);
192 } else {
193 char ch = 0;
194
195 ch = *((char *) gxemul_hw_map.kbd);
196 if (ch == '\r') {
197 ch = '\n';
198 }
199 if (ch == 0x7f) {
200 ch = '\b';
201 }
202 chardev_push_character(&console, ch);
203 }
204}
205
206static irq_ownership_t gxemul_claim(void)
207{
208 return IRQ_ACCEPT;
209}
210
211
212/** Acquire console back for kernel. */
213void gxemul_grab_console(void)
214{
215 ipl_t ipl = interrupts_disable();
216 spinlock_lock(&gxemul_console_irq.lock);
217 gxemul_console_irq.notif_cfg.notify = false;
218 spinlock_unlock(&gxemul_console_irq.lock);
219 interrupts_restore(ipl);
220}
221
222/** Return console to userspace. */
223void gxemul_release_console(void)
224{
225 ipl_t ipl = interrupts_disable();
226 spinlock_lock(&gxemul_console_irq.lock);
227 if (gxemul_console_irq.notif_cfg.answerbox) {
228 gxemul_console_irq.notif_cfg.notify = true;
229 }
230 spinlock_unlock(&gxemul_console_irq.lock);
231 interrupts_restore(ipl);
232}
233
234/** Initializes console object representing gxemul console.
235 *
236 * @param devno device number.
237 */
238void gxemul_console_init(devno_t devno)
239{
240 chardev_initialize("gxemul_console", &console, &gxemul_ops);
241 stdin = &console;
242 stdout = &console;
243
244 irq_initialize(&gxemul_console_irq);
245 gxemul_console_irq.devno = devno;
246 gxemul_console_irq.inr = GXEMUL_KBD_IRQ;
247 gxemul_console_irq.claim = gxemul_claim;
248 gxemul_console_irq.handler = gxemul_irq_handler;
249 irq_register(&gxemul_console_irq);
250
251 gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
252
253 sysinfo_set_item_val("kbd", NULL, true);
254 sysinfo_set_item_val("kbd.devno", NULL, devno);
255 sysinfo_set_item_val("kbd.inr", NULL, GXEMUL_KBD_IRQ);
256 sysinfo_set_item_val("kbd.address.virtual", NULL, gxemul_hw_map.kbd);
257}
258
259/** Starts gxemul Real Time Clock device, which asserts regular interrupts.
260 *
261 * @param frequency Interrupts frequency (0 disables RTC).
262 */
263static void gxemul_timer_start(uint32_t frequency)
264{
265 *((uint32_t*) gxemul_hw_map.rtc_freq) = frequency;
266}
267
268static irq_ownership_t gxemul_timer_claim(void)
269{
270 return IRQ_ACCEPT;
271}
272
273/** Timer interrupt handler.
274 *
275 * @param irq Interrupt information.
276 * @param arg Not used.
277 */
278static void gxemul_timer_irq_handler(irq_t *irq, void *arg, ...)
279{
280 /*
281 * We are holding a lock which prevents preemption.
282 * Release the lock, call clock() and reacquire the lock again.
283 */
284 spinlock_unlock(&irq->lock);
285 clock();
286 spinlock_lock(&irq->lock);
287
288 /* acknowledge tick */
289 *((uint32_t*) gxemul_hw_map.rtc_ack) = 0;
290}
291
292/** Initializes and registers timer interrupt handler. */
293static void gxemul_timer_irq_init(void)
294{
295 irq_initialize(&gxemul_timer_irq);
296 gxemul_timer_irq.devno = device_assign_devno();
297 gxemul_timer_irq.inr = GXEMUL_TIMER_IRQ;
298 gxemul_timer_irq.claim = gxemul_timer_claim;
299 gxemul_timer_irq.handler = gxemul_timer_irq_handler;
300
301 irq_register(&gxemul_timer_irq);
302}
303
304
305/** Starts timer.
306 *
307 * Initiates regular timer interrupts after initializing
308 * corresponding interrupt handler.
309 */
310void gxemul_timer_irq_start(void)
311{
312 gxemul_timer_irq_init();
313 gxemul_timer_start(GXEMUL_TIMER_FREQ);
314}
315
316/** Returns the size of emulated memory.
317 *
318 * @return Size in bytes.
319 */
320size_t gxemul_get_memory_size(void)
321{
322 return *((int *) (GXEMUL_MP + GXEMUL_MP_MEMSIZE_OFFSET));
323}
324
325/** Prints a character.
326 *
327 * @param ch Character to be printed.
328 */
329void gxemul_debug_putc(char ch)
330{
331 char *addr = 0;
332 if (!hw_map_init_called) {
333 addr = (char *) GXEMUL_KBD;
334 } else {
335 addr = (char *) gxemul_hw_map.videoram;
336 }
337
338 *(addr) = ch;
339}
340
341/** Stops gxemul. */
342void gxemul_cpu_halt(void)
343{
344 char * addr = 0;
345 if (!hw_map_init_called) {
346 addr = (char *) GXEMUL_KBD;
347 } else {
348 addr = (char *) gxemul_hw_map.videoram;
349 }
350
351 *(addr + GXEMUL_HALT_OFFSET) = '\0';
352}
353
354/** Gxemul specific interrupt exception handler.
355 *
356 * Determines sources of the interrupt from interrupt controller and
357 * calls high-level handlers for them.
358 *
359 * @param exc_no Interrupt exception number.
360 * @param istate Saved processor state.
361 */
362void gxemul_irq_exception(int exc_no, istate_t *istate)
363{
364 uint32_t sources = gxemul_irqc_get_sources();
365 int i;
366
367 for (i = 0; i < GXEMUL_IRQC_MAX_IRQ; i++) {
368 if (sources & (1 << i)) {
369 irq_t *irq = irq_dispatch_and_lock(i);
370 if (irq) {
371 /* The IRQ handler was found. */
372 irq->handler(irq, irq->arg);
373 spinlock_unlock(&irq->lock);
374 } else {
375 /* Spurious interrupt.*/
376 dprintf("cpu%d: spurious interrupt (inum=%d)\n",
377 CPU->id, i);
378 }
379 }
380 }
381}
382
383/** Returns address of framebuffer device.
384 *
385 * @return Address of framebuffer device.
386 */
387uintptr_t gxemul_get_fb_address(void)
388{
389 return (uintptr_t) GXEMUL_FB;
390}
391
392/** @}
393 */
Note: See TracBrowser for help on using the repository browser.