Subversion Repositories HelenOS

Rev

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

Rev Author Line No. Line
2215 kebrt 1
/*
2263 kebrt 2
 * Copyright (c) 2007 Michal Kebrt, Petr Stepan
2215 kebrt 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
 
2419 kebrt 29
/** @addtogroup arm32gxemul
2215 kebrt 30
 * @{
31
 */
32
/** @file
2410 stepan 33
 *  @brief GXemul drivers.
2215 kebrt 34
 */
35
 
36
#include <interrupt.h>
37
#include <console/chardev.h>
38
#include <arch/drivers/gxemul.h>
39
#include <console/console.h>
40
#include <sysinfo/sysinfo.h>
41
#include <print.h>
2245 stepan 42
#include <ddi/device.h>
2263 kebrt 43
#include <mm/page.h>
2274 kebrt 44
#include <arch/machine.h>
2326 kebrt 45
#include <arch/debug/print.h>
2215 kebrt 46
 
2414 kebrt 47
/* Addresses of devices. */
2263 kebrt 48
#define GXEMUL_VIDEORAM            0x10000000
49
#define GXEMUL_KBD                 0x10000000
2300 kebrt 50
#define GXEMUL_HALT_OFFSET         0x10
2263 kebrt 51
#define GXEMUL_RTC                 0x15000000
52
#define GXEMUL_RTC_FREQ_OFFSET     0x100
53
#define GXEMUL_RTC_ACK_OFFSET      0x110
54
#define GXEMUL_IRQC                0x16000000
55
#define GXEMUL_IRQC_MASK_OFFSET    0x4
56
#define GXEMUL_IRQC_UNMASK_OFFSET  0x8
57
#define GXEMUL_MP                  0x11000000
58
#define GXEMUL_MP_MEMSIZE_OFFSET   0x0090
2340 kebrt 59
#define GXEMUL_FB                  0x12000000
2215 kebrt 60
 
2414 kebrt 61
/* IRQs */
2245 stepan 62
#define GXEMUL_KBD_IRQ      2
63
#define GXEMUL_TIMER_IRQ    4
64
 
2263 kebrt 65
static gxemul_hw_map_t gxemul_hw_map;
2215 kebrt 66
static chardev_t console;
2414 kebrt 67
static irq_t gxemul_console_irq;
2245 stepan 68
static irq_t gxemul_timer_irq;
2215 kebrt 69
 
2290 kebrt 70
static bool hw_map_init_called = false;
71
 
2414 kebrt 72
static void gxemul_kbd_enable(chardev_t *dev);
73
static void gxemul_kbd_disable(chardev_t *dev);
4342 svoboda 74
static void gxemul_write(chardev_t *dev, const char ch, bool silent);
2215 kebrt 75
static char gxemul_do_read(chardev_t *dev);
76
 
77
static chardev_operations_t gxemul_ops = {
2414 kebrt 78
    .resume = gxemul_kbd_enable,
79
    .suspend = gxemul_kbd_disable,
2215 kebrt 80
    .write = gxemul_write,
81
    .read = gxemul_do_read,
82
};
83
 
2357 kebrt 84
 
2355 stepan 85
/** Returns the mask of active interrupts. */
2340 kebrt 86
static inline uint32_t gxemul_irqc_get_sources(void)
87
{
2464 jermar 88
    return *((uint32_t *) gxemul_hw_map.irqc);
2340 kebrt 89
}
2263 kebrt 90
 
2357 kebrt 91
 
2340 kebrt 92
/** Masks interrupt.
93
 *
94
 * @param irq interrupt number
95
 */
96
static inline void gxemul_irqc_mask(uint32_t irq)
97
{
2464 jermar 98
    *((uint32_t *) gxemul_hw_map.irqc_mask) = irq;
2340 kebrt 99
}
100
 
2357 kebrt 101
 
2340 kebrt 102
/** Unmasks interrupt.
103
 *
104
 * @param irq interrupt number
105
 */
106
static inline void gxemul_irqc_unmask(uint32_t irq)
107
{
2464 jermar 108
    *((uint32_t *) gxemul_hw_map.irqc_unmask) = irq;
2340 kebrt 109
}
110
 
2357 kebrt 111
 
2263 kebrt 112
/** Initializes #gxemul_hw_map. */
2358 kebrt 113
void gxemul_hw_map_init(void)
2263 kebrt 114
{
115
    gxemul_hw_map.videoram = hw_map(GXEMUL_VIDEORAM, PAGE_SIZE);
116
    gxemul_hw_map.kbd = hw_map(GXEMUL_KBD, PAGE_SIZE);
117
    gxemul_hw_map.rtc = hw_map(GXEMUL_RTC, PAGE_SIZE);
118
    gxemul_hw_map.irqc = hw_map(GXEMUL_IRQC, PAGE_SIZE);
119
 
120
    gxemul_hw_map.rtc_freq = gxemul_hw_map.rtc + GXEMUL_RTC_FREQ_OFFSET;
121
    gxemul_hw_map.rtc_ack = gxemul_hw_map.rtc + GXEMUL_RTC_ACK_OFFSET;
122
    gxemul_hw_map.irqc_mask = gxemul_hw_map.irqc + GXEMUL_IRQC_MASK_OFFSET;
2464 jermar 123
    gxemul_hw_map.irqc_unmask = gxemul_hw_map.irqc +
124
        GXEMUL_IRQC_UNMASK_OFFSET;
2264 kebrt 125
 
2290 kebrt 126
    hw_map_init_called = true;
2263 kebrt 127
}
128
 
2357 kebrt 129
 
2414 kebrt 130
/** Putchar that works with gxemul.
131
 *
132
 * @param dev Not used.
133
 * @param ch Characted to be printed.
134
 */
4341 svoboda 135
static void gxemul_write(chardev_t *dev, const char ch, bool silent)
2215 kebrt 136
{
4341 svoboda 137
    if (!silent)
138
        *((char *) gxemul_hw_map.videoram) = ch;
2215 kebrt 139
}
140
 
2414 kebrt 141
/** Enables gxemul keyboard (interrupt unmasked).
142
 *
143
 * @param dev Not used.
144
 *
145
 * Called from getc().
146
 */
147
static void gxemul_kbd_enable(chardev_t *dev)
2215 kebrt 148
{
2245 stepan 149
    gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
2215 kebrt 150
}
151
 
2414 kebrt 152
/** Disables gxemul keyboard (interrupt masked).
153
 *
154
 * @param dev not used
155
 *
156
 * Called from getc().
157
 */
158
static void gxemul_kbd_disable(chardev_t *dev)
2215 kebrt 159
{
2245 stepan 160
    gxemul_irqc_mask(GXEMUL_KBD_IRQ);
2215 kebrt 161
}
162
 
2357 kebrt 163
/** Read character using polling, assume interrupts disabled.
164
 *
165
 *  @param dev Not used.
166
 */
2215 kebrt 167
static char gxemul_do_read(chardev_t *dev)
168
{
169
    char ch;
170
 
171
    while (1) {
2263 kebrt 172
        ch = *((volatile char *) gxemul_hw_map.kbd);
2215 kebrt 173
        if (ch) {
174
            if (ch == '\r')
175
                return '\n';
176
            if (ch == 0x7f)
177
                return '\b';
178
            return ch;
179
        }
180
    }
181
}
182
 
2414 kebrt 183
/** Process keyboard interrupt.
184
 *  
185
 *  @param irq IRQ information.
186
 */
4343 svoboda 187
static void gxemul_irq_handler(irq_t *irq)
2215 kebrt 188
{
4344 svoboda 189
    char ch = 0;
2215 kebrt 190
 
4344 svoboda 191
    ch = *((char *) gxemul_hw_map.kbd);
192
    if (ch == '\r') {
193
        ch = '\n';
2215 kebrt 194
    }
4344 svoboda 195
    if (ch == 0x7f) {
196
        ch = '\b';
197
    }
198
    chardev_push_character(&console, ch);
2215 kebrt 199
}
200
 
4344 svoboda 201
static irq_ownership_t gxemul_claim(irq_t *irq)
2215 kebrt 202
{
203
    return IRQ_ACCEPT;
204
}
205
 
2357 kebrt 206
/** Initializes console object representing gxemul console.
207
 *
2418 kebrt 208
 *  @param devno device number.
2357 kebrt 209
 */
2358 kebrt 210
void gxemul_console_init(devno_t devno)
2215 kebrt 211
{
2263 kebrt 212
    chardev_initialize("gxemul_console", &console, &gxemul_ops);
2215 kebrt 213
    stdin = &console;
214
    stdout = &console;
215
 
2414 kebrt 216
    irq_initialize(&gxemul_console_irq);
217
    gxemul_console_irq.devno = devno;
218
    gxemul_console_irq.inr = GXEMUL_KBD_IRQ;
219
    gxemul_console_irq.claim = gxemul_claim;
220
    gxemul_console_irq.handler = gxemul_irq_handler;
221
    irq_register(&gxemul_console_irq);
2215 kebrt 222
 
2245 stepan 223
    gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
2215 kebrt 224
 
225
    sysinfo_set_item_val("kbd", NULL, true);
226
    sysinfo_set_item_val("kbd.devno", NULL, devno);
227
    sysinfo_set_item_val("kbd.inr", NULL, GXEMUL_KBD_IRQ);
2263 kebrt 228
    sysinfo_set_item_val("kbd.address.virtual", NULL, gxemul_hw_map.kbd);
2215 kebrt 229
}
230
 
2245 stepan 231
/** Starts gxemul Real Time Clock device, which asserts regular interrupts.
232
 *
2357 kebrt 233
 * @param frequency Interrupts frequency (0 disables RTC).
2245 stepan 234
 */
2340 kebrt 235
static void gxemul_timer_start(uint32_t frequency)
2245 stepan 236
{
2464 jermar 237
    *((uint32_t*) gxemul_hw_map.rtc_freq) = frequency;
2245 stepan 238
}
239
 
4344 svoboda 240
static irq_ownership_t gxemul_timer_claim(irq_t *irq)
2245 stepan 241
{
242
    return IRQ_ACCEPT;
243
}
244
 
2355 stepan 245
/** Timer interrupt handler.
246
 *
2357 kebrt 247
 * @param irq Interrupt information.
248
 * @param arg Not used.
2355 stepan 249
 */
4343 svoboda 250
static void gxemul_timer_irq_handler(irq_t *irq)
2245 stepan 251
{
252
    /*
253
    * We are holding a lock which prevents preemption.
254
    * Release the lock, call clock() and reacquire the lock again.
255
    */
256
    spinlock_unlock(&irq->lock);
2286 stepan 257
    clock();
2245 stepan 258
    spinlock_lock(&irq->lock);
2261 stepan 259
 
260
    /* acknowledge tick */
2464 jermar 261
    *((uint32_t*) gxemul_hw_map.rtc_ack) = 0;
2245 stepan 262
}
263
 
2357 kebrt 264
/** Initializes and registers timer interrupt handler. */
2464 jermar 265
static void gxemul_timer_irq_init(void)
2245 stepan 266
{
267
    irq_initialize(&gxemul_timer_irq);
268
    gxemul_timer_irq.devno = device_assign_devno();
269
    gxemul_timer_irq.inr = GXEMUL_TIMER_IRQ;
270
    gxemul_timer_irq.claim = gxemul_timer_claim;
271
    gxemul_timer_irq.handler = gxemul_timer_irq_handler;
2261 stepan 272
 
2245 stepan 273
    irq_register(&gxemul_timer_irq);
274
}
275
 
2357 kebrt 276
 
2355 stepan 277
/** Starts timer.
278
 *
279
 * Initiates regular timer interrupts after initializing
280
 * corresponding interrupt handler.
281
 */
2464 jermar 282
void gxemul_timer_irq_start(void)
2263 kebrt 283
{
2274 kebrt 284
    gxemul_timer_irq_init();
285
    gxemul_timer_start(GXEMUL_TIMER_FREQ);
286
}
287
 
2355 stepan 288
/** Returns the size of emulated memory.
289
 *
2357 kebrt 290
 * @return Size in bytes.
2355 stepan 291
 */
2358 kebrt 292
size_t gxemul_get_memory_size(void)
2274 kebrt 293
{
2464 jermar 294
    return  *((int *) (GXEMUL_MP + GXEMUL_MP_MEMSIZE_OFFSET));
2263 kebrt 295
}
2245 stepan 296
 
2412 kebrt 297
/** Prints a character.
298
 *
299
 *  @param ch Character to be printed.
300
 */
2358 kebrt 301
void gxemul_debug_putc(char ch)
2300 kebrt 302
{
2464 jermar 303
    char *addr = 0;
2290 kebrt 304
    if (!hw_map_init_called) {
2300 kebrt 305
        addr = (char *) GXEMUL_KBD;
2290 kebrt 306
    } else {
2300 kebrt 307
        addr = (char *) gxemul_hw_map.videoram;
2290 kebrt 308
    }
2300 kebrt 309
 
310
    *(addr) = ch;
2264 kebrt 311
}
312
 
2355 stepan 313
/** Stops gxemul. */
2358 kebrt 314
void gxemul_cpu_halt(void)
2300 kebrt 315
{
316
    char * addr = 0;
317
    if (!hw_map_init_called) {
318
        addr = (char *) GXEMUL_KBD;
319
    } else {
320
        addr = (char *) gxemul_hw_map.videoram;
321
    }
322
 
323
    *(addr + GXEMUL_HALT_OFFSET) = '\0';
324
}
325
 
2355 stepan 326
/** Gxemul specific interrupt exception handler.
327
 *
328
 * Determines sources of the interrupt from interrupt controller and
329
 * calls high-level handlers for them.
330
 *
2357 kebrt 331
 * @param exc_no Interrupt exception number.
332
 * @param istate Saved processor state.
2355 stepan 333
 */
2358 kebrt 334
void gxemul_irq_exception(int exc_no, istate_t *istate)
2306 kebrt 335
{
336
    uint32_t sources = gxemul_irqc_get_sources();
2464 jermar 337
    int i;
338
 
339
    for (i = 0; i < GXEMUL_IRQC_MAX_IRQ; i++) {
2306 kebrt 340
        if (sources & (1 << i)) {
341
            irq_t *irq = irq_dispatch_and_lock(i);
342
            if (irq) {
343
                /* The IRQ handler was found. */
4343 svoboda 344
                irq->handler(irq);
2306 kebrt 345
                spinlock_unlock(&irq->lock);
346
            } else {
347
                /* Spurious interrupt.*/
2464 jermar 348
                dprintf("cpu%d: spurious interrupt (inum=%d)\n",
349
                    CPU->id, i);
2306 kebrt 350
            }
351
        }
352
    }
353
}
354
 
2412 kebrt 355
/** Returns address of framebuffer device.
356
 *
357
 *  @return Address of framebuffer device.
358
 */
2358 kebrt 359
uintptr_t gxemul_get_fb_address(void)
2357 kebrt 360
{
2360 kebrt 361
    return (uintptr_t) GXEMUL_FB;
2357 kebrt 362
}
363
 
2215 kebrt 364
/** @}
365
 */