source: mainline/kernel/arch/sparc64/src/drivers/sgcn.c@ 64c7e14

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 64c7e14 was 965dc18, checked in by Jakub Jermar <jakub@…>, 17 years ago

Merge sparc branch to trunk.

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