source: mainline/kernel/arch/sparc64/src/drivers/sgcn.c@ 91ea7c4

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

Some additional fixes to the sgcn driver.

  • Property mode set to 100644
File size: 11.1 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.h>
38#include <arch/drivers/sgcn.h>
39#include <arch/drivers/kbd.h>
40#include <genarch/ofw/ofw_tree.h>
41#include <debug.h>
42#include <string.h>
43#include <print.h>
44#include <mm/page.h>
45#include <proc/thread.h>
46#include <console/chardev.h>
47#include <console/console.h>
48#include <sysinfo/sysinfo.h>
49#include <synch/spinlock.h>
50
51#define POLL_INTERVAL 10000
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 necessary arrangements 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 * 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) \
99 ((type *) (sgcn_buffer_begin + (offset)))
100
101/** Returns a pointer to the console buffer header. */
102#define SGCN_BUFFER_HEADER (SGCN_BUFFER(sgcn_buffer_header_t, 0))
103
104/** defined in drivers/kbd.c */
105extern kbd_type_t kbd_type;
106
107/** starting address of SRAM, will be set by the init_sram_begin function */
108static uintptr_t sram_begin;
109
110/**
111 * starting address of the SGCN buffer, will be set by the
112 * init_sgcn_buffer_begin function
113 */
114static uintptr_t sgcn_buffer_begin;
115
116/* true iff the kernel driver should ignore pressed keys */
117static bool kbd_disabled;
118
119/*
120 * Ensures that writing to the buffer and consequent update of the write pointer
121 * are together one atomic operation.
122 */
123SPINLOCK_INITIALIZE(sgcn_output_lock);
124
125/*
126 * Prevents the input buffer read/write pointers from getting to inconsistent
127 * state.
128 */
129SPINLOCK_INITIALIZE(sgcn_input_lock);
130
131
132/* functions referenced from definitions of I/O operations structures */
133static void sgcn_noop(chardev_t *);
134static void sgcn_putchar(chardev_t *, const char, bool);
135static char sgcn_key_read(chardev_t *);
136
137/** character device operations */
138static chardev_operations_t sgcn_ops = {
139 .suspend = sgcn_noop,
140 .resume = sgcn_noop,
141 .read = sgcn_key_read,
142 .write = sgcn_putchar
143};
144
145/** SGCN character device */
146chardev_t sgcn_io;
147
148/** Address of the chardev, which is connected to SGCN. */
149static chardev_t *sgcnout;
150
151/**
152 * Set some sysinfo values (SRAM address and SRAM size).
153 */
154static void register_sram(uintptr_t sram_begin_physical)
155{
156 sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
157 sysinfo_set_item_val("sram.address.physical", NULL,
158 sram_begin_physical);
159}
160
161/**
162 * Initializes the starting address of SRAM.
163 *
164 * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
165 * physical memory, where C is the value read from the "iosram-toc"
166 * property of the "/chosen" OBP node. The sram_begin variable will
167 * be set to the virtual address which maps to the SRAM physical
168 * address.
169 */
170static void init_sram_begin(void)
171{
172 ofw_tree_node_t *chosen;
173 ofw_tree_property_t *iosram_toc;
174 uintptr_t sram_begin_physical;
175
176 chosen = ofw_tree_lookup("/chosen");
177 if (!chosen)
178 panic("Cannot find '/chosen'.");
179
180 iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
181 if (!iosram_toc)
182 panic("Cannot find property 'iosram-toc'.");
183 if (!iosram_toc->value)
184 panic("Cannot find SRAM TOC.");
185
186 sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
187 + *((uint32_t *) iosram_toc->value);
188 sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
189
190 register_sram(sram_begin_physical);
191}
192
193/**
194 * Initializes the starting address of the SGCN buffer.
195 *
196 * The offset of the SGCN buffer within SRAM is obtained from the
197 * SRAM table of contents. The table of contents contains
198 * information about several buffers, among which there is an OBP
199 * console buffer - this one will be used as the SGCN buffer.
200 *
201 * This function also writes the offset of the SGCN buffer within SRAM
202 * under the sram.buffer.offset sysinfo key.
203 */
204static void sgcn_buffer_begin_init(void)
205{
206 init_sram_begin();
207
208 ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
209
210 /* lookup TOC entry with the correct key */
211 uint32_t i;
212 for (i = 0; i < MAX_TOC_ENTRIES; i++) {
213 if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
214 break;
215 }
216 ASSERT(i < MAX_TOC_ENTRIES);
217
218 sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
219
220 sysinfo_set_item_val("sram.buffer.offset", NULL,
221 SRAM_TOC->keys[i].offset);
222}
223
224/**
225 * Default suspend/resume operation for the input device.
226 */
227static void sgcn_noop(chardev_t *d)
228{
229}
230
231/**
232 * Writes a single character to the SGCN (circular) output buffer
233 * and updates the output write pointer so that SGCN gets to know
234 * that the character has been written.
235 */
236static void sgcn_do_putchar(const char c)
237{
238 uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
239 uint32_t end = SGCN_BUFFER_HEADER->out_end;
240 uint32_t size = end - begin;
241
242 /* we need pointers to volatile variables */
243 volatile char *buf_ptr = (volatile char *)
244 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
245 volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
246 volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
247
248 /*
249 * Write the character and increment the write pointer modulo the
250 * output buffer size. Note that if we are to rewrite a character
251 * which has not been read by the SGCN controller yet (i.e. the output
252 * buffer is full), we need to wait until the controller reads some more
253 * characters. We wait actively, which means that all threads waiting
254 * for the lock are blocked. However, this situation is
255 * 1) rare - the output buffer is big, so filling the whole
256 * output buffer is improbable
257 * 2) short-lasting - it will take the controller only a fraction
258 * of millisecond to pick the unread characters up
259 * 3) not serious - the blocked threads are those that print something
260 * to user console, which is not a time-critical operation
261 */
262 uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
263 while (*out_rdptr_ptr == new_wrptr)
264 ;
265 *buf_ptr = c;
266 *out_wrptr_ptr = new_wrptr;
267}
268
269/**
270 * SGCN output operation. Prints a single character to the SGCN. If the line
271 * feed character is written ('\n'), the carriage return character ('\r') is
272 * written straight away.
273 */
274static void sgcn_putchar(struct chardev * cd, const char c, bool silent)
275{
276 if (!silent) {
277 spinlock_lock(&sgcn_output_lock);
278
279 sgcn_do_putchar(c);
280 if (c == '\n')
281 sgcn_do_putchar('\r');
282
283 spinlock_unlock(&sgcn_output_lock);
284 }
285}
286
287/**
288 * Called when actively reading the character. Not implemented yet.
289 */
290static char sgcn_key_read(chardev_t *d)
291{
292 return (char) 0;
293}
294
295/**
296 * Grabs the input for kernel.
297 */
298void sgcn_grab(void)
299{
300 kbd_disabled = true;
301}
302
303/**
304 * Releases the input so that userspace can use it.
305 */
306void sgcn_release(void)
307{
308 kbd_disabled = true;
309}
310
311/**
312 * Function regularly called by the keyboard polling thread. Finds out whether
313 * there are some unread characters in the input queue. If so, it picks them up
314 * and sends them to the upper layers of HelenOS.
315 */
316static void sgcn_poll()
317{
318 uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
319 uint32_t end = SGCN_BUFFER_HEADER->in_end;
320 uint32_t size = end - begin;
321
322 if (kbd_disabled)
323 return;
324
325 spinlock_lock(&sgcn_input_lock);
326
327 /* we need pointers to volatile variables */
328 volatile char *buf_ptr = (volatile char *)
329 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
330 volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
331 volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
332
333 while (*in_rdptr_ptr != *in_wrptr_ptr) {
334
335 buf_ptr = (volatile char *)
336 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
337 char c = *buf_ptr;
338 *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
339
340 if (sgcnout)
341 chardev_push_character(sgcnout, c);
342 }
343
344 spinlock_unlock(&sgcn_input_lock);
345}
346
347/**
348 * Polling thread function.
349 */
350static void kkbdpoll(void *arg) {
351 while (1) {
352 if (!silent) {
353 sgcn_poll();
354 }
355 thread_usleep(POLL_INTERVAL);
356 }
357}
358
359/**
360 * A public function which initializes I/O from/to Serengeti console
361 * and sets it as a default input/output.
362 */
363void sgcn_init(chardev_t *devout)
364{
365 sgcn_buffer_begin_init();
366
367 kbd_type = KBD_SGCN;
368
369 sysinfo_set_item_val("kbd", NULL, true);
370 sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
371 sysinfo_set_item_val("fb.kind", NULL, 4);
372
373 thread_t *t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true);
374 if (!t)
375 panic("Cannot create kkbdpoll.");
376 thread_ready(t);
377
378 chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
379 stdout = &sgcn_io;
380
381 sgcnout = devout;
382}
383
384/** @}
385 */
Note: See TracBrowser for help on using the repository browser.