Subversion Repositories HelenOS

Rev

Rev 4111 | Rev 4203 | Go to most recent revision | 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
 
4070 rimsky 37
#include <arch.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>
4011 svoboda 42
#include <string.h>
3502 rimsky 43
#include <print.h>
44
#include <mm/page.h>
4070 rimsky 45
#include <proc/thread.h>
3502 rimsky 46
#include <console/chardev.h>
47
#include <console/console.h>
3549 rimsky 48
#include <sysinfo/sysinfo.h>
3502 rimsky 49
#include <synch/spinlock.h>
50
 
4070 rimsky 51
#define POLL_INTERVAL       10000
52
 
3502 rimsky 53
/*
54
 * Physical address at which the SBBC starts. This value has been obtained
55
 * by inspecting (using Simics) memory accesses made by OBP. It is valid
56
 * for the Simics-simulated Serengeti machine. The author of this code is
57
 * not sure whether this value is valid generally.
58
 */
59
#define SBBC_START      0x63000000000
60
 
61
/* offset of SRAM within the SBBC memory */
62
#define SBBC_SRAM_OFFSET    0x900000
63
 
3549 rimsky 64
/* size (in bytes) of the physical memory area which will be mapped */
65
#define MAPPED_AREA_SIZE    (128 * 1024)
66
 
3502 rimsky 67
/* magic string contained at the beginning of SRAM */
68
#define SRAM_TOC_MAGIC      "TOCSRAM"
69
 
70
/*
71
 * Key into the SRAM table of contents which identifies the entry
72
 * describing the OBP console buffer. It is worth mentioning
73
 * that the OBP console buffer is not the only console buffer
74
 * which can be used. It is, however, used because when the kernel
75
 * is running, the OBP buffer is not used by OBP any more but OBP
4071 jermar 76
 * has already made necessary arrangements so that the output will
3502 rimsky 77
 * be read from the OBP buffer and input will go to the OBP buffer.
78
 * Therefore HelenOS needs to make no such arrangements any more.
79
 */
80
#define CONSOLE_KEY     "OBPCONS"
81
 
82
/* magic string contained at the beginning of the console buffer */
83
#define SGCN_BUFFER_MAGIC   "CON"
84
 
85
/*
86
 * Returns a pointer to the object of a given type which is placed at the given
87
 * offset from the SRAM beginning.
88
 */
89
#define SRAM(type, offset)  ((type *) (sram_begin + (offset)))
90
 
91
/* Returns a pointer to the SRAM table of contents. */
92
#define SRAM_TOC        (SRAM(iosram_toc_t, 0))
93
 
94
/*
95
 * Returns a pointer to the object of a given type which is placed at the given
96
 * offset from the console buffer beginning.
97
 */
98
#define SGCN_BUFFER(type, offset) \
4071 jermar 99
    ((type *) (sgcn_buffer_begin + (offset)))
3502 rimsky 100
 
3549 rimsky 101
/** Returns a pointer to the console buffer header. */
3502 rimsky 102
#define SGCN_BUFFER_HEADER  (SGCN_BUFFER(sgcn_buffer_header_t, 0))
103
 
3549 rimsky 104
/** defined in drivers/kbd.c */
105
extern kbd_type_t kbd_type;
3502 rimsky 106
 
3549 rimsky 107
/** starting address of SRAM, will be set by the init_sram_begin function */
3502 rimsky 108
static uintptr_t sram_begin;
109
 
3549 rimsky 110
/**
3502 rimsky 111
 * starting address of the SGCN buffer, will be set by the
112
 * init_sgcn_buffer_begin function
113
 */
114
static uintptr_t sgcn_buffer_begin;
115
 
4070 rimsky 116
/* true iff the kernel driver should ignore pressed keys */
117
static bool kbd_disabled;
3502 rimsky 118
 
119
/*
120
 * Ensures that writing to the buffer and consequent update of the write pointer
121
 * are together one atomic operation.
122
 */
123
SPINLOCK_INITIALIZE(sgcn_output_lock);
124
 
3514 rimsky 125
/*
3549 rimsky 126
 * Prevents the input buffer read/write pointers from getting to inconsistent
127
 * state.
3514 rimsky 128
 */
129
SPINLOCK_INITIALIZE(sgcn_input_lock);
3502 rimsky 130
 
131
 
3514 rimsky 132
/* functions referenced from definitions of I/O operations structures */
4202 decky 133
static void sgcn_putchar(outdev_t *, const wchar_t, bool);
3514 rimsky 134
 
4111 jermar 135
/** SGCN output device operations */
136
static outdev_operations_t sgcnout_ops = {
3549 rimsky 137
    .write = sgcn_putchar
3502 rimsky 138
};
139
 
4111 jermar 140
/** SGCN input device operations */
141
static indev_operations_t sgcnin_ops = {
142
    .poll = NULL
143
};
3514 rimsky 144
 
4111 jermar 145
static indev_t sgcnin;      /**< SGCN input device. */
146
static outdev_t sgcnout;    /**< SGCN output device. */
4071 jermar 147
 
3549 rimsky 148
/**
3908 decky 149
 * Set some sysinfo values (SRAM address and SRAM size).
3549 rimsky 150
 */
3908 decky 151
static void register_sram(uintptr_t sram_begin_physical)
3549 rimsky 152
{
153
    sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
3618 rimsky 154
    sysinfo_set_item_val("sram.address.physical", NULL,
4071 jermar 155
        sram_begin_physical);
3549 rimsky 156
}
3514 rimsky 157
 
3502 rimsky 158
/**
159
 * Initializes the starting address of SRAM.
160
 *
161
 * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
162
 * physical memory, where C is the value read from the "iosram-toc"
163
 * property of the "/chosen" OBP node. The sram_begin variable will
164
 * be set to the virtual address which maps to the SRAM physical
3549 rimsky 165
 * address.
3502 rimsky 166
 */
167
static void init_sram_begin(void)
168
{
169
    ofw_tree_node_t *chosen;
170
    ofw_tree_property_t *iosram_toc;
3549 rimsky 171
    uintptr_t sram_begin_physical;
3502 rimsky 172
 
173
    chosen = ofw_tree_lookup("/chosen");
174
    if (!chosen)
3790 svoboda 175
        panic("Cannot find '/chosen'.");
3502 rimsky 176
 
177
    iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
178
    if (!iosram_toc)
3790 svoboda 179
        panic("Cannot find property 'iosram-toc'.");
3502 rimsky 180
    if (!iosram_toc->value)
3790 svoboda 181
        panic("Cannot find SRAM TOC.");
3502 rimsky 182
 
3549 rimsky 183
    sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
4071 jermar 184
        + *((uint32_t *) iosram_toc->value);
3549 rimsky 185
    sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
186
 
3908 decky 187
    register_sram(sram_begin_physical);
3502 rimsky 188
}
189
 
190
/**
191
 * Initializes the starting address of the SGCN buffer.
192
 *
193
 * The offset of the SGCN buffer within SRAM is obtained from the
194
 * SRAM table of contents. The table of contents contains
195
 * information about several buffers, among which there is an OBP
196
 * console buffer - this one will be used as the SGCN buffer.
3549 rimsky 197
 *
198
 * This function also writes the offset of the SGCN buffer within SRAM
199
 * under the sram.buffer.offset sysinfo key.
3502 rimsky 200
 */
201
static void sgcn_buffer_begin_init(void)
202
{
4111 jermar 203
    static bool initialized;
204
 
205
    if (initialized)
206
        return;
207
 
3502 rimsky 208
    init_sram_begin();
209
 
210
    ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
211
 
212
    /* lookup TOC entry with the correct key */
213
    uint32_t i;
214
    for (i = 0; i < MAX_TOC_ENTRIES; i++) {
215
        if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
216
            break;
217
    }
218
    ASSERT(i < MAX_TOC_ENTRIES);
219
 
220
    sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
3549 rimsky 221
 
222
    sysinfo_set_item_val("sram.buffer.offset", NULL,
4071 jermar 223
        SRAM_TOC->keys[i].offset);
4111 jermar 224
 
225
    initialized = true;
3502 rimsky 226
}
227
 
228
/**
229
 * Writes a single character to the SGCN (circular) output buffer
230
 * and updates the output write pointer so that SGCN gets to know
231
 * that the character has been written.
232
 */
233
static void sgcn_do_putchar(const char c)
234
{
235
    uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
236
    uint32_t end = SGCN_BUFFER_HEADER->out_end;
237
    uint32_t size = end - begin;
238
 
239
    /* we need pointers to volatile variables */
240
    volatile char *buf_ptr = (volatile char *)
4071 jermar 241
        SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
3502 rimsky 242
    volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
243
    volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
244
 
245
    /*
246
     * Write the character and increment the write pointer modulo the
247
     * output buffer size. Note that if we are to rewrite a character
248
     * which has not been read by the SGCN controller yet (i.e. the output
249
     * buffer is full), we need to wait until the controller reads some more
250
     * characters. We wait actively, which means that all threads waiting
251
     * for the lock are blocked. However, this situation is
252
     *   1) rare - the output buffer is big, so filling the whole
253
     *             output buffer is improbable
254
     *   2) short-lasting - it will take the controller only a fraction
255
     *             of millisecond to pick the unread characters up
256
     *   3) not serious - the blocked threads are those that print something
257
     *             to user console, which is not a time-critical operation
258
     */
259
    uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
260
    while (*out_rdptr_ptr == new_wrptr)
261
        ;
262
    *buf_ptr = c;
263
    *out_wrptr_ptr = new_wrptr;
264
}
265
 
266
/**
267
 * SGCN output operation. Prints a single character to the SGCN. If the line
268
 * feed character is written ('\n'), the carriage return character ('\r') is
269
 * written straight away.
270
 */
4202 decky 271
static void sgcn_putchar(outdev_t *od, const wchar_t ch, bool silent)
3502 rimsky 272
{
3844 decky 273
    if (!silent) {
274
        spinlock_lock(&sgcn_output_lock);
275
 
4202 decky 276
        if (ascii_check(ch)) {
277
            sgcn_do_putchar(ch);
278
            if (ch == '\n')
279
                sgcn_do_putchar('\r');
280
        } else
281
            sgcn_do_putchar(invalch);
3844 decky 282
 
283
        spinlock_unlock(&sgcn_output_lock);
3502 rimsky 284
    }
285
}
286
 
287
/**
3549 rimsky 288
 * Grabs the input for kernel.
289
 */
290
void sgcn_grab(void)
291
{
4070 rimsky 292
    kbd_disabled = true;
3549 rimsky 293
}
294
 
295
/**
296
 * Releases the input so that userspace can use it.
297
 */
298
void sgcn_release(void)
299
{
4070 rimsky 300
    kbd_disabled = true;
3549 rimsky 301
}
302
 
303
/**
3514 rimsky 304
 * Function regularly called by the keyboard polling thread. Finds out whether
305
 * there are some unread characters in the input queue. If so, it picks them up
306
 * and sends them to the upper layers of HelenOS.
307
 */
4070 rimsky 308
static void sgcn_poll()
3514 rimsky 309
{
310
    uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
311
    uint32_t end = SGCN_BUFFER_HEADER->in_end;
312
    uint32_t size = end - begin;
4070 rimsky 313
 
4071 jermar 314
    if (kbd_disabled)
315
        return;
316
 
3549 rimsky 317
    spinlock_lock(&sgcn_input_lock);
318
 
3514 rimsky 319
    /* we need pointers to volatile variables */
320
    volatile char *buf_ptr = (volatile char *)
4071 jermar 321
        SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
3514 rimsky 322
    volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
323
    volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
324
 
325
    while (*in_rdptr_ptr != *in_wrptr_ptr) {
3549 rimsky 326
 
3514 rimsky 327
        buf_ptr = (volatile char *)
4071 jermar 328
            SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
3549 rimsky 329
        char c = *buf_ptr;
330
        *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
331
 
4111 jermar 332
        indev_push_character(&sgcnin, c);  
3549 rimsky 333
    }  
4070 rimsky 334
 
3514 rimsky 335
    spinlock_unlock(&sgcn_input_lock);
336
}
337
 
338
/**
4070 rimsky 339
 * Polling thread function.
340
 */
341
static void kkbdpoll(void *arg) {
342
    while (1) {
343
        if (!silent) {
344
            sgcn_poll();
345
        }
346
        thread_usleep(POLL_INTERVAL);
347
    }
348
}
349
 
350
/**
4111 jermar 351
 * A public function which initializes input from the Serengeti console.
3502 rimsky 352
 */
4111 jermar 353
indev_t *sgcnin_init(void)
3502 rimsky 354
{
355
    sgcn_buffer_begin_init();
3549 rimsky 356
 
357
    kbd_type = KBD_SGCN;
358
 
359
    sysinfo_set_item_val("kbd", NULL, true);
360
    sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
4070 rimsky 361
 
362
    thread_t *t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true);
363
    if (!t)
364
        panic("Cannot create kkbdpoll.");
365
    thread_ready(t);
3502 rimsky 366
 
4111 jermar 367
    indev_initialize("sgcnin", &sgcnin, &sgcnin_ops);
4071 jermar 368
 
4111 jermar 369
    return &sgcnin;
3502 rimsky 370
}
371
 
4111 jermar 372
/**
373
 * A public function which initializes output to the Serengeti console.
374
 */
375
void sgcnout_init(void)
376
{
377
    sgcn_buffer_begin_init();
378
 
379
    sysinfo_set_item_val("fb.kind", NULL, 4);
380
 
381
    outdev_initialize("sgcnout", &sgcnout, &sgcnout_ops);  
382
    stdout = &sgcnout;
383
}
384
 
3502 rimsky 385
/** @}
3549 rimsky 386
 */