Subversion Repositories HelenOS

Rev

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