Subversion Repositories HelenOS

Rev

Rev 4612 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3528 pillai 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 arm32qemu_icp
30
 * @{
31
 */
32
/** @file
33
 *  @brief QEMU icp drivers.
34
 */
35
 
36
#include <interrupt.h>
37
#include <ipc/irq.h>
38
#include <console/chardev.h>
39
#include <arch/drivers/qemu.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>
3529 pillai 47
#include <genarch/fb/fb.h>
48
#include <genarch/fb/visuals.h>
3528 pillai 49
 
50
/* Addresses of devices. */
51
#define QEMU_ICP_VIDEORAM            0x16000000
52
#define QEMU_ICP_KBD                 0x18000000
53
#define QEMU_ICP_HALT_OFFSET         0x10
54
#define QEMU_ICP_RTC                 0x13000000
4612 pillai 55
#define QEMU_ICP_RTC1_LOAD_OFFSET    0x100
56
#define QEMU_ICP_RTC1_READ_OFFSET    0x104
57
#define QEMU_ICP_RTC1_CTL_OFFSET     0x108
58
#define QEMU_ICP_RTC1_INTRCLR_OFFSET 0x10C
59
#define QEMU_ICP_RTC1_BGLOAD_OFFSET  0x118
60
#define QEMU_ICP_RTC_CTL_VALUE       0x00E2
3528 pillai 61
#define QEMU_ICP_IRQC                0x14000000
3759 pillai 62
#define QEMU_ICP_IRQC_MASK_OFFSET    0xC
63
#define QEMU_ICP_IRQC_UNMASK_OFFSET  0x8
3528 pillai 64
#define QEMU_ICP_MP                  0x11000000
65
#define QEMU_ICP_MP_MEMSIZE_OFFSET   0x0090
66
#define QEMU_ICP_FB                  0x94000
67
 
68
#define ICP_VGA              0xC0000000
69
#define ICP_CMCR             0x10000000
70
 
71
/* IRQs */
4612 pillai 72
#define QEMU_ICP_KBD_IRQ        3
73
#define QEMU_ICP_TIMER_IRQ      6
3528 pillai 74
 
75
static qemu_icp_hw_map_t qemu_icp_hw_map;
76
static chardev_t console;
77
static irq_t qemu_icp_console_irq;
78
static irq_t qemu_icp_timer_irq;
79
 
80
static bool hw_map_init_called = false;
81
static bool vga_init = false;
82
 
83
static void qemu_icp_kbd_enable(chardev_t *dev);
84
static void qemu_icp_kbd_disable(chardev_t *dev);
85
static void qemu_icp_write(chardev_t *dev, const char ch);
86
static char qemu_icp_do_read(chardev_t *dev);
87
void icp_vga_init(void);
88
 
89
static chardev_operations_t qemu_icp_ops = {
90
    .resume = qemu_icp_kbd_enable,
91
    .suspend = qemu_icp_kbd_disable,
92
    .write = qemu_icp_write,
93
    .read = qemu_icp_do_read,
94
};
95
 
96
/** Initializes the vga
97
 *
98
 */
99
void icp_vga_init(void)
100
{
101
    *(uint32_t*)((char *)(qemu_icp_hw_map.cmcr)+0x14) = 0xA05F0000;
102
    *(uint32_t*)((char *)(qemu_icp_hw_map.cmcr)+0x1C) = 0x12C11000;
103
    *(uint32_t*)qemu_icp_hw_map.vga = 0x3F1F3F9C;
104
    *(uint32_t*)((char *)(qemu_icp_hw_map.vga) + 0x4) = 0x080B61DF;
105
    *(uint32_t*)((char *)(qemu_icp_hw_map.vga) + 0x8) = 0x067F3800;
106
    *(uint32_t*)((char *)(qemu_icp_hw_map.vga) + 0x10) = QEMU_ICP_FB;
107
    *(uint32_t *)((char *)(qemu_icp_hw_map.vga) + 0x1C) = 0x182B;
108
    *(uint32_t*)((char *)(qemu_icp_hw_map.cmcr)+0xC) = 0x33805000;
109
 
110
}
111
 
112
/** Returns the mask of active interrupts. */
113
static inline uint32_t qemu_icp_irqc_get_sources(void)
114
{
115
    return *((uint32_t *) qemu_icp_hw_map.irqc);
116
}
117
 
118
 
119
/** Masks interrupt.
120
 *
121
 * @param irq interrupt number
122
 */
123
static inline void qemu_icp_irqc_mask(uint32_t irq)
124
{
4612 pillai 125
    *((uint32_t *) qemu_icp_hw_map.irqc_mask) = (1 << irq);
3528 pillai 126
}
127
 
128
 
129
/** Unmasks interrupt.
130
 *
131
 * @param irq interrupt number
132
 */
133
static inline void qemu_icp_irqc_unmask(uint32_t irq)
134
{
4612 pillai 135
    *((uint32_t *) qemu_icp_hw_map.irqc_unmask) |= (1 << irq);
3528 pillai 136
}
137
 
3529 pillai 138
/** Initializes the icp frame buffer */
139
void qemu_icp_fb_init(void)
140
{
141
    fb_init(qemu_icp_get_fb_address(), 640, 480, 2560, VISUAL_BGR_8_8_8_0);
142
}
3528 pillai 143
 
144
/** Initializes #qemu_icp_hw_map. */
145
void qemu_icp_hw_map_init(void)
146
{
147
    qemu_icp_hw_map.videoram = hw_map(QEMU_ICP_VIDEORAM, PAGE_SIZE);
148
    qemu_icp_hw_map.kbd = hw_map(QEMU_ICP_KBD, PAGE_SIZE);
149
    qemu_icp_hw_map.rtc = hw_map(QEMU_ICP_RTC, PAGE_SIZE);
4612 pillai 150
    qemu_icp_hw_map.rtc1_load = qemu_icp_hw_map.rtc + QEMU_ICP_RTC1_LOAD_OFFSET;
151
    qemu_icp_hw_map.rtc1_read = qemu_icp_hw_map.rtc + QEMU_ICP_RTC1_READ_OFFSET;
152
    qemu_icp_hw_map.rtc1_ctl = qemu_icp_hw_map.rtc + QEMU_ICP_RTC1_CTL_OFFSET;
153
    qemu_icp_hw_map.rtc1_intrclr = qemu_icp_hw_map.rtc + QEMU_ICP_RTC1_INTRCLR_OFFSET;
154
    qemu_icp_hw_map.rtc1_bgload = qemu_icp_hw_map.rtc + QEMU_ICP_RTC1_BGLOAD_OFFSET;
155
 
3528 pillai 156
    qemu_icp_hw_map.irqc = hw_map(QEMU_ICP_IRQC, PAGE_SIZE);
157
    qemu_icp_hw_map.irqc_mask = qemu_icp_hw_map.irqc + QEMU_ICP_IRQC_MASK_OFFSET;
158
    qemu_icp_hw_map.irqc_unmask = qemu_icp_hw_map.irqc +
159
        QEMU_ICP_IRQC_UNMASK_OFFSET;
160
    qemu_icp_hw_map.cmcr = hw_map(ICP_CMCR, PAGE_SIZE);
161
    qemu_icp_hw_map.vga = hw_map(ICP_VGA, PAGE_SIZE);
162
 
163
    //icp_vga_init();
164
 
165
    hw_map_init_called = true;
166
}
167
 
168
 
169
/** Putchar that works with qemu_icp.
170
 *
171
 * @param dev Not used.
172
 * @param ch Characted to be printed.
173
 */
174
static void qemu_icp_write(chardev_t *dev, const char ch)
175
{
176
    *((char *) qemu_icp_hw_map.videoram) = ch;
177
}
178
 
179
/** Enables qemu_icp keyboard (interrupt unmasked).
180
 *
181
 * @param dev Not used.
182
 *
183
 * Called from getc().
184
 */
185
static void qemu_icp_kbd_enable(chardev_t *dev)
186
{
187
    qemu_icp_irqc_unmask(QEMU_ICP_KBD_IRQ);
188
}
189
 
190
/** Disables qemu_icp keyboard (interrupt masked).
191
 *
192
 * @param dev not used
193
 *
194
 * Called from getc().
195
 */
196
static void qemu_icp_kbd_disable(chardev_t *dev)
197
{
198
    qemu_icp_irqc_mask(QEMU_ICP_KBD_IRQ);
199
}
200
 
201
/** Read character using polling, assume interrupts disabled.
202
 *
203
 *  @param dev Not used.
204
 */
205
static char qemu_icp_do_read(chardev_t *dev)
206
{
207
    char ch;
208
 
209
    while (1) {
210
        ch = *((volatile char *) qemu_icp_hw_map.kbd);
211
        if (ch) {
212
            if (ch == '\r')
213
                return '\n';
214
            if (ch == 0x7f)
215
                return '\b';
216
            return ch;
217
        }
218
    }
219
}
220
 
221
/** Process keyboard interrupt.
222
 *  
223
 *  @param irq IRQ information.
224
 *  @param arg Not used.
225
 */
226
static void qemu_icp_irq_handler(irq_t *irq, void *arg, ...)
227
{
228
    if ((irq->notif_cfg.notify) && (irq->notif_cfg.answerbox)) {
229
        ipc_irq_send_notif(irq);
230
    } else {
231
        char ch = 0;
232
 
233
        ch = *((char *) qemu_icp_hw_map.kbd);
234
        if (ch == '\r') {
235
            ch = '\n';
236
        }
237
        if (ch == 0x7f) {
238
            ch = '\b';
239
        }
240
        chardev_push_character(&console, ch);
241
    }
242
}
243
 
244
static irq_ownership_t qemu_icp_claim(void)
245
{
246
    return IRQ_ACCEPT;
247
}
248
 
249
 
250
/** Acquire console back for kernel. */
251
void qemu_icp_grab_console(void)
252
{
253
    ipl_t ipl = interrupts_disable();
254
    spinlock_lock(&qemu_icp_console_irq.lock);
255
    qemu_icp_console_irq.notif_cfg.notify = false;
256
    spinlock_unlock(&qemu_icp_console_irq.lock);
257
    interrupts_restore(ipl);
258
}
259
 
260
/** Return console to userspace. */
261
void qemu_icp_release_console(void)
262
{
263
    ipl_t ipl = interrupts_disable();
264
    spinlock_lock(&qemu_icp_console_irq.lock);
265
    if (qemu_icp_console_irq.notif_cfg.answerbox) {
266
        qemu_icp_console_irq.notif_cfg.notify = true;
267
    }
268
    spinlock_unlock(&qemu_icp_console_irq.lock);
269
    interrupts_restore(ipl);
270
}
271
 
272
/** Initializes console object representing qemu_icp console.
273
 *
274
 *  @param devno device number.
275
 */
276
void qemu_icp_console_init(devno_t devno)
277
{
4615 pillai 278
    qemu_icp_irqc_mask(QEMU_ICP_KBD_IRQ);
3528 pillai 279
    chardev_initialize("qemu_icp_console", &console, &qemu_icp_ops);
280
    stdin = &console;
281
    stdout = &console;
282
 
283
    irq_initialize(&qemu_icp_console_irq);
284
    qemu_icp_console_irq.devno = devno;
285
    qemu_icp_console_irq.inr = QEMU_ICP_KBD_IRQ;
286
    qemu_icp_console_irq.claim = qemu_icp_claim;
287
    qemu_icp_console_irq.handler = qemu_icp_irq_handler;
288
    irq_register(&qemu_icp_console_irq);
289
 
290
    qemu_icp_irqc_unmask(QEMU_ICP_KBD_IRQ);
291
 
292
    sysinfo_set_item_val("kbd", NULL, true);
293
    sysinfo_set_item_val("kbd.devno", NULL, devno);
294
    sysinfo_set_item_val("kbd.inr", NULL, QEMU_ICP_KBD_IRQ);
295
    sysinfo_set_item_val("kbd.address.virtual", NULL, qemu_icp_hw_map.kbd);
296
}
297
 
298
/** Starts qemu_icp Real Time Clock device, which asserts regular interrupts.
299
 *
300
 * @param frequency Interrupts frequency (0 disables RTC).
301
 */
302
static void qemu_icp_timer_start(uint32_t frequency)
303
{
4615 pillai 304
    qemu_icp_irqc_mask(QEMU_ICP_TIMER_IRQ);
4612 pillai 305
    *((uint32_t*) qemu_icp_hw_map.rtc1_load) = frequency;
306
    *((uint32_t*) qemu_icp_hw_map.rtc1_bgload) = frequency;
307
    *((uint32_t*) qemu_icp_hw_map.rtc1_ctl) = QEMU_ICP_RTC_CTL_VALUE;
308
    qemu_icp_irqc_unmask(QEMU_ICP_TIMER_IRQ);
3528 pillai 309
}
310
 
311
static irq_ownership_t qemu_icp_timer_claim(void)
312
{
4615 pillai 313
    *((uint32_t*) qemu_icp_hw_map.rtc1_intrclr) = 1;
3528 pillai 314
    return IRQ_ACCEPT;
315
}
316
 
317
/** Timer interrupt handler.
318
 *
319
 * @param irq Interrupt information.
320
 * @param arg Not used.
321
 */
322
static void qemu_icp_timer_irq_handler(irq_t *irq, void *arg, ...)
323
{
324
    /*
325
    * We are holding a lock which prevents preemption.
326
    * Release the lock, call clock() and reacquire the lock again.
327
    */
328
    spinlock_unlock(&irq->lock);
329
    clock();
330
    spinlock_lock(&irq->lock);
331
 
332
}
333
 
334
/** Initializes and registers timer interrupt handler. */
335
static void qemu_icp_timer_irq_init(void)
336
{
337
    irq_initialize(&qemu_icp_timer_irq);
338
    qemu_icp_timer_irq.devno = device_assign_devno();
339
    qemu_icp_timer_irq.inr = QEMU_ICP_TIMER_IRQ;
340
    qemu_icp_timer_irq.claim = qemu_icp_timer_claim;
341
    qemu_icp_timer_irq.handler = qemu_icp_timer_irq_handler;
342
 
343
    irq_register(&qemu_icp_timer_irq);
344
}
345
 
346
 
347
/** Starts timer.
348
 *
349
 * Initiates regular timer interrupts after initializing
350
 * corresponding interrupt handler.
351
 */
352
void qemu_icp_timer_irq_start(void)
353
{
354
    qemu_icp_timer_irq_init();
355
    qemu_icp_timer_start(QEMU_ICP_TIMER_FREQ);
356
}
357
 
358
/** Returns the size of emulated memory.
359
 *
360
 * @return Size in bytes.
361
 */
362
size_t qemu_icp_get_memory_size(void)
363
{
364
    //return  *((int *) (QEMU_ICP_MP + QEMU_ICP_MP_MEMSIZE_OFFSET));
365
    return 0x2000000;
366
}
367
 
368
/** Prints a character.
369
 *
370
 *  @param ch Character to be printed.
371
 */
372
void qemu_icp_debug_putc(char ch)
373
{
374
    char *addr = 0;
375
    if (!hw_map_init_called) {
376
        addr = (char *) QEMU_ICP_KBD;
377
    } else {
378
        addr = (char *) qemu_icp_hw_map.videoram;
379
    }
380
 
381
    if (ch == '\n')
382
        *(addr) = '\r';
383
    *(addr) = ch;
384
}
385
 
386
/** Stops qemu_icp. */
387
void qemu_icp_cpu_halt(void)
388
{
389
    char * addr = 0;
390
    if (!hw_map_init_called) {
391
        addr = (char *) QEMU_ICP_KBD;
392
    } else {
393
        addr = (char *) qemu_icp_hw_map.videoram;
394
    }
395
 
396
    *(addr + QEMU_ICP_HALT_OFFSET) = '\0';
397
}
398
 
399
/** Gxemul specific interrupt exception handler.
400
 *
401
 * Determines sources of the interrupt from interrupt controller and
402
 * calls high-level handlers for them.
403
 *
404
 * @param exc_no Interrupt exception number.
405
 * @param istate Saved processor state.
406
 */
407
void qemu_icp_irq_exception(int exc_no, istate_t *istate)
408
{
409
    uint32_t sources = qemu_icp_irqc_get_sources();
410
    int i;
411
 
412
    for (i = 0; i < QEMU_ICP_IRQC_MAX_IRQ; i++) {
413
        if (sources & (1 << i)) {
414
            irq_t *irq = irq_dispatch_and_lock(i);
415
            if (irq) {
416
                /* The IRQ handler was found. */
417
                irq->handler(irq, irq->arg);
418
                spinlock_unlock(&irq->lock);
419
            } else {
420
                /* Spurious interrupt.*/
421
                dprintf("cpu%d: spurious interrupt (inum=%d)\n",
422
                    CPU->id, i);
423
            }
424
        }
425
    }
426
}
427
 
428
/** Returns address of framebuffer device.
429
 *
430
 *  @return Address of framebuffer device.
431
 */
432
uintptr_t qemu_icp_get_fb_address(void)
433
{
434
    if (!vga_init) {
435
        icp_vga_init();
436
        vga_init = true;
437
    }
438
    return (uintptr_t) QEMU_ICP_FB;
439
}
440
 
441
 
442
/** @}
443
 */