Subversion Repositories HelenOS

Rev

Rev 3618 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3502 rimsky 1
/*
2
 * Copyright (c) 2008 Pavel Rimsky
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 sparc64
30
 * @{
31
 */
32
/**
33
 * @file
34
 * @brief   SGCN driver.
35
 */
36
 
3862 rimsky 37
#include <arch/asm.h>
3502 rimsky 38
#include <arch/drivers/sgcn.h>
3549 rimsky 39
#include <arch/drivers/kbd.h>
3502 rimsky 40
#include <genarch/ofw/ofw_tree.h>
41
#include <debug.h>
42
#include <func.h>
43
#include <print.h>
44
#include <mm/page.h>
3549 rimsky 45
#include <ipc/irq.h>
46
#include <ddi/ddi.h>
47
#include <ddi/device.h>
3502 rimsky 48
#include <console/chardev.h>
49
#include <console/console.h>
3549 rimsky 50
#include <ddi/device.h>
51
#include <sysinfo/sysinfo.h>
3502 rimsky 52
#include <synch/spinlock.h>
53
 
54
/*
55
 * Physical address at which the SBBC starts. This value has been obtained
56
 * by inspecting (using Simics) memory accesses made by OBP. It is valid
57
 * for the Simics-simulated Serengeti machine. The author of this code is
58
 * not sure whether this value is valid generally.
59
 */
60
#define SBBC_START      0x63000000000
61
 
62
/* offset of SRAM within the SBBC memory */
63
#define SBBC_SRAM_OFFSET    0x900000
64
 
3549 rimsky 65
/* size (in bytes) of the physical memory area which will be mapped */
66
#define MAPPED_AREA_SIZE    (128 * 1024)
67
 
3502 rimsky 68
/* magic string contained at the beginning of SRAM */
69
#define SRAM_TOC_MAGIC      "TOCSRAM"
70
 
71
/*
72
 * Key into the SRAM table of contents which identifies the entry
73
 * describing the OBP console buffer. It is worth mentioning
74
 * that the OBP console buffer is not the only console buffer
75
 * which can be used. It is, however, used because when the kernel
76
 * is running, the OBP buffer is not used by OBP any more but OBP
77
 * has already made neccessary arangements so that the output will
78
 * be read from the OBP buffer and input will go to the OBP buffer.
79
 * Therefore HelenOS needs to make no such arrangements any more.
80
 */
81
#define CONSOLE_KEY     "OBPCONS"
82
 
83
/* magic string contained at the beginning of the console buffer */
84
#define SGCN_BUFFER_MAGIC   "CON"
85
 
3549 rimsky 86
/**
87
 * The driver is polling based, but in order to notify the userspace
88
 * of a key being pressed, we need to supply the interface with some
89
 * interrupt number. The interrupt number can be arbitrary as it it
90
 * will never be used for identifying HW interrupts, but only in
91
 * notifying the userspace.
92
 */
93
#define FICTIONAL_INR       1
3502 rimsky 94
 
3549 rimsky 95
 
3502 rimsky 96
/*
97
 * Returns a pointer to the object of a given type which is placed at the given
98
 * offset from the SRAM beginning.
99
 */
100
#define SRAM(type, offset)  ((type *) (sram_begin + (offset)))
101
 
102
/* Returns a pointer to the SRAM table of contents. */
103
#define SRAM_TOC        (SRAM(iosram_toc_t, 0))
104
 
105
/*
106
 * Returns a pointer to the object of a given type which is placed at the given
107
 * offset from the console buffer beginning.
108
 */
109
#define SGCN_BUFFER(type, offset) \
110
                ((type *) (sgcn_buffer_begin + (offset)))
111
 
3549 rimsky 112
/** Returns a pointer to the console buffer header. */
3502 rimsky 113
#define SGCN_BUFFER_HEADER  (SGCN_BUFFER(sgcn_buffer_header_t, 0))
114
 
3549 rimsky 115
/** defined in drivers/kbd.c */
116
extern kbd_type_t kbd_type;
3502 rimsky 117
 
3549 rimsky 118
/** starting address of SRAM, will be set by the init_sram_begin function */
3502 rimsky 119
static uintptr_t sram_begin;
120
 
3549 rimsky 121
/**
3502 rimsky 122
 * starting address of the SGCN buffer, will be set by the
123
 * init_sgcn_buffer_begin function
124
 */
125
static uintptr_t sgcn_buffer_begin;
126
 
3549 rimsky 127
/**
128
 * SGCN IRQ structure. So far used only for notifying the userspace of the
129
 * key being pressed, not for kernel being informed about keyboard interrupts.
130
 */
131
static irq_t sgcn_irq;
3502 rimsky 132
 
3549 rimsky 133
// TODO think of a way how to synchronize accesses to SGCN buffer between the kernel and the userspace
134
 
3502 rimsky 135
/*
136
 * Ensures that writing to the buffer and consequent update of the write pointer
137
 * are together one atomic operation.
138
 */
139
SPINLOCK_INITIALIZE(sgcn_output_lock);
140
 
3514 rimsky 141
/*
3549 rimsky 142
 * Prevents the input buffer read/write pointers from getting to inconsistent
143
 * state.
3514 rimsky 144
 */
145
SPINLOCK_INITIALIZE(sgcn_input_lock);
3502 rimsky 146
 
147
 
3514 rimsky 148
/* functions referenced from definitions of I/O operations structures */
149
static void sgcn_noop(chardev_t *);
150
static void sgcn_putchar(chardev_t *, const char);
151
static char sgcn_key_read(chardev_t *);
152
 
3549 rimsky 153
/** character device operations */
154
static chardev_operations_t sgcn_ops = {
3514 rimsky 155
    .suspend = sgcn_noop,
156
    .resume = sgcn_noop,
3549 rimsky 157
    .read = sgcn_key_read,
158
    .write = sgcn_putchar
3502 rimsky 159
};
160
 
3549 rimsky 161
/** SGCN character device */
162
chardev_t sgcn_io;
3514 rimsky 163
 
3549 rimsky 164
/**
165
 * Registers the physical area of the SRAM so that the userspace SGCN
166
 * driver can map it. Moreover, it sets some sysinfo values (SRAM address
167
 * and SRAM size).
168
 */
169
static void register_sram_parea(uintptr_t sram_begin_physical)
170
{
171
    static parea_t sram_parea;
172
    sram_parea.pbase = sram_begin_physical;
173
    sram_parea.vbase = (uintptr_t) sram_begin;
174
    sram_parea.frames = MAPPED_AREA_SIZE / FRAME_SIZE;
175
    sram_parea.cacheable = false;
176
    ddi_parea_register(&sram_parea);
177
 
178
    sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
3618 rimsky 179
    sysinfo_set_item_val("sram.address.physical", NULL,
180
        sram_begin_physical);
3549 rimsky 181
}
3514 rimsky 182
 
3502 rimsky 183
/**
184
 * Initializes the starting address of SRAM.
185
 *
186
 * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
187
 * physical memory, where C is the value read from the "iosram-toc"
188
 * property of the "/chosen" OBP node. The sram_begin variable will
189
 * be set to the virtual address which maps to the SRAM physical
3549 rimsky 190
 * address.
191
 *
192
 * It also registers the physical area of SRAM and sets some sysinfo
193
 * values (SRAM address and SRAM size).
3502 rimsky 194
 */
195
static void init_sram_begin(void)
196
{
197
    ofw_tree_node_t *chosen;
198
    ofw_tree_property_t *iosram_toc;
3549 rimsky 199
    uintptr_t sram_begin_physical;
3502 rimsky 200
 
201
    chosen = ofw_tree_lookup("/chosen");
202
    if (!chosen)
203
        panic("Can't find /chosen.\n");
204
 
205
    iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
206
    if (!iosram_toc)
207
        panic("Can't find property \"iosram-toc\".\n");
208
    if (!iosram_toc->value)
209
        panic("Can't find SRAM TOC.\n");
210
 
3549 rimsky 211
    sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
212
        + *((uint32_t *) iosram_toc->value);
213
    sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
214
 
215
    register_sram_parea(sram_begin_physical);
3502 rimsky 216
}
217
 
218
/**
219
 * Initializes the starting address of the SGCN buffer.
220
 *
221
 * The offset of the SGCN buffer within SRAM is obtained from the
222
 * SRAM table of contents. The table of contents contains
223
 * information about several buffers, among which there is an OBP
224
 * console buffer - this one will be used as the SGCN buffer.
3549 rimsky 225
 *
226
 * This function also writes the offset of the SGCN buffer within SRAM
227
 * under the sram.buffer.offset sysinfo key.
3502 rimsky 228
 */
229
static void sgcn_buffer_begin_init(void)
230
{
231
    init_sram_begin();
232
 
233
    ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
234
 
235
    /* lookup TOC entry with the correct key */
236
    uint32_t i;
237
    for (i = 0; i < MAX_TOC_ENTRIES; i++) {
238
        if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
239
            break;
240
    }
241
    ASSERT(i < MAX_TOC_ENTRIES);
242
 
243
    sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
3549 rimsky 244
 
245
    sysinfo_set_item_val("sram.buffer.offset", NULL,
246
        SRAM_TOC->keys[i].offset);
3502 rimsky 247
}
248
 
249
/**
3514 rimsky 250
 * Default suspend/resume operation for the input device.
251
 */
252
static void sgcn_noop(chardev_t *d)
253
{
254
}
255
 
256
/**
3502 rimsky 257
 * Writes a single character to the SGCN (circular) output buffer
258
 * and updates the output write pointer so that SGCN gets to know
259
 * that the character has been written.
260
 */
261
static void sgcn_do_putchar(const char c)
262
{
263
    uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
264
    uint32_t end = SGCN_BUFFER_HEADER->out_end;
265
    uint32_t size = end - begin;
266
 
267
    /* we need pointers to volatile variables */
268
    volatile char *buf_ptr = (volatile char *)
269
        SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
270
    volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
271
    volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
272
 
273
    /*
274
     * Write the character and increment the write pointer modulo the
275
     * output buffer size. Note that if we are to rewrite a character
276
     * which has not been read by the SGCN controller yet (i.e. the output
277
     * buffer is full), we need to wait until the controller reads some more
278
     * characters. We wait actively, which means that all threads waiting
279
     * for the lock are blocked. However, this situation is
280
     *   1) rare - the output buffer is big, so filling the whole
281
     *             output buffer is improbable
282
     *   2) short-lasting - it will take the controller only a fraction
283
     *             of millisecond to pick the unread characters up
284
     *   3) not serious - the blocked threads are those that print something
285
     *             to user console, which is not a time-critical operation
286
     */
287
    uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
288
    while (*out_rdptr_ptr == new_wrptr)
289
        ;
290
    *buf_ptr = c;
291
    *out_wrptr_ptr = new_wrptr;
292
}
293
 
294
/**
295
 * SGCN output operation. Prints a single character to the SGCN. If the line
296
 * feed character is written ('\n'), the carriage return character ('\r') is
297
 * written straight away.
298
 */
299
static void sgcn_putchar(struct chardev * cd, const char c)
300
{
301
    spinlock_lock(&sgcn_output_lock);
302
 
303
    sgcn_do_putchar(c);
304
    if (c == '\n') {
305
        sgcn_do_putchar('\r');
306
    }
307
 
308
    spinlock_unlock(&sgcn_output_lock);
309
}
310
 
311
/**
3514 rimsky 312
 * Called when actively reading the character. Not implemented yet.
313
 */
314
static char sgcn_key_read(chardev_t *d)
315
{
316
    return (char) 0;
317
}
318
 
319
/**
3549 rimsky 320
 * The driver works in polled mode, so no interrupt should be handled by it.
321
 */
322
static irq_ownership_t sgcn_claim(void)
323
{
324
    return IRQ_DECLINE;
325
}
326
 
327
/**
328
 * The driver works in polled mode, so no interrupt should be handled by it.
329
 */
330
static void sgcn_irq_handler(irq_t *irq, void *arg, ...)
331
{
332
    panic("Not yet implemented, SGCN works in polled mode.\n");
333
}
334
 
335
/**
336
 * Grabs the input for kernel.
337
 */
338
void sgcn_grab(void)
339
{
340
    ipl_t ipl = interrupts_disable();
341
 
342
    volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
343
    volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
344
 
345
    /* skip all the user typed before the grab and hasn't been processed */
346
    spinlock_lock(&sgcn_input_lock);
347
    *in_rdptr_ptr = *in_wrptr_ptr;
348
    spinlock_unlock(&sgcn_input_lock);
349
 
350
    spinlock_lock(&sgcn_irq.lock);
351
    sgcn_irq.notif_cfg.notify = false;
352
    spinlock_unlock(&sgcn_irq.lock);
353
 
354
    interrupts_restore(ipl);
355
}
356
 
357
/**
358
 * Releases the input so that userspace can use it.
359
 */
360
void sgcn_release(void)
361
{
362
    ipl_t ipl = interrupts_disable();
363
    spinlock_lock(&sgcn_irq.lock);
364
    if (sgcn_irq.notif_cfg.answerbox)
365
        sgcn_irq.notif_cfg.notify = true;
366
    spinlock_unlock(&sgcn_irq.lock);
367
    interrupts_restore(ipl);
368
}
369
 
370
/**
3514 rimsky 371
 * Function regularly called by the keyboard polling thread. Finds out whether
372
 * there are some unread characters in the input queue. If so, it picks them up
373
 * and sends them to the upper layers of HelenOS.
374
 */
375
void sgcn_poll(void)
376
{
377
    uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
378
    uint32_t end = SGCN_BUFFER_HEADER->in_end;
379
    uint32_t size = end - begin;
380
 
3549 rimsky 381
    spinlock_lock(&sgcn_input_lock);
382
 
383
    ipl_t ipl = interrupts_disable();
384
    spinlock_lock(&sgcn_irq.lock);
385
 
3514 rimsky 386
    /* we need pointers to volatile variables */
387
    volatile char *buf_ptr = (volatile char *)
388
        SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
389
    volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
390
    volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
391
 
3549 rimsky 392
    if (*in_rdptr_ptr != *in_wrptr_ptr) {
393
        if (sgcn_irq.notif_cfg.notify && sgcn_irq.notif_cfg.answerbox) {
394
            ipc_irq_send_notif(&sgcn_irq);
395
            spinlock_unlock(&sgcn_irq.lock);
396
            interrupts_restore(ipl);
397
            spinlock_unlock(&sgcn_input_lock);
398
            return;
399
        }
400
    }
401
 
402
    spinlock_unlock(&sgcn_irq.lock);
403
    interrupts_restore(ipl);   
404
 
3514 rimsky 405
    while (*in_rdptr_ptr != *in_wrptr_ptr) {
3549 rimsky 406
 
3514 rimsky 407
        buf_ptr = (volatile char *)
408
            SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
3549 rimsky 409
        char c = *buf_ptr;
410
        *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
411
 
412
        if (c == '\r') {
413
            c = '\n';
414
        }
415
        chardev_push_character(&sgcn_io, c);   
416
    }  
3514 rimsky 417
 
418
    spinlock_unlock(&sgcn_input_lock);
419
}
420
 
421
/**
3502 rimsky 422
 * A public function which initializes I/O from/to Serengeti console
423
 * and sets it as a default input/output.
424
 */
425
void sgcn_init(void)
426
{
427
    sgcn_buffer_begin_init();
3549 rimsky 428
 
429
    kbd_type = KBD_SGCN;
430
 
431
    devno_t devno = device_assign_devno();
432
    irq_initialize(&sgcn_irq);
433
    sgcn_irq.devno = devno;
434
    sgcn_irq.inr = FICTIONAL_INR;
435
    sgcn_irq.claim = sgcn_claim;
436
    sgcn_irq.handler = sgcn_irq_handler;
437
    irq_register(&sgcn_irq);
3502 rimsky 438
 
3549 rimsky 439
    sysinfo_set_item_val("kbd", NULL, true);
440
    sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
441
    sysinfo_set_item_val("kbd.devno", NULL, devno);
442
    sysinfo_set_item_val("kbd.inr", NULL, FICTIONAL_INR);
443
    sysinfo_set_item_val("fb.kind", NULL, 4);
3502 rimsky 444
 
3549 rimsky 445
    chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
446
    stdin = &sgcn_io;
447
    stdout = &sgcn_io;
3502 rimsky 448
}
449
 
450
/** @}
3549 rimsky 451
 */