source: mainline/kernel/arch/sparc64/src/drivers/sgcn.c@ 86018c1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 86018c1 was a71c158, checked in by Martin Decky <martin@…>, 16 years ago

kernel output devices now suport multiple instances (except ski and sgcn, which respect the same interface, but behave as singletons)
if more than one output device gets initialized, the output is cloned to all of them
get rid of arch_grab_console() and arch_release_console() (output devices can implement a generic "redraw" method, input devices respect the "silent" global variable)
related cleanups and modifications

  • Property mode set to 100644
File size: 10.4 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 *) (instance->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 *) (instance->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
104static void sgcn_putchar(outdev_t *, const wchar_t, bool);
105
106static outdev_operations_t sgcndev_ops = {
107 .write = sgcn_putchar,
108 .redraw = NULL
109};
110
111static sgcn_instance_t *instance = NULL;
112
113/**
114 * Set some sysinfo values (SRAM address and SRAM size).
115 */
116static void register_sram(uintptr_t sram_begin_physical)
117{
118 sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
119 sysinfo_set_item_val("sram.address.physical", NULL,
120 sram_begin_physical);
121}
122
123/**
124 * Initializes the starting address of SRAM.
125 *
126 * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
127 * physical memory, where C is the value read from the "iosram-toc"
128 * property of the "/chosen" OBP node. The sram_begin variable will
129 * be set to the virtual address which maps to the SRAM physical
130 * address.
131 */
132static void init_sram_begin(void)
133{
134 ASSERT(instance)
135
136 ofw_tree_node_t *chosen = ofw_tree_lookup("/chosen");
137 if (!chosen)
138 panic("Cannot find '/chosen'.");
139
140 ofw_tree_property_t *iosram_toc =
141 ofw_tree_getprop(chosen, "iosram-toc");
142 if (!iosram_toc)
143 panic("Cannot find property 'iosram-toc'.");
144 if (!iosram_toc->value)
145 panic("Cannot find SRAM TOC.");
146
147 uintptr_t sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
148 + *((uint32_t *) iosram_toc->value);
149 instance->sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
150
151 register_sram(sram_begin_physical);
152}
153
154/**
155 * Function regularly called by the keyboard polling thread. Finds out whether
156 * there are some unread characters in the input queue. If so, it picks them up
157 * and sends them to the upper layers of HelenOS.
158 */
159static void sgcn_poll(sgcn_instance_t *instance)
160{
161 uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
162 uint32_t end = SGCN_BUFFER_HEADER->in_end;
163 uint32_t size = end - begin;
164
165 if (silent)
166 return;
167
168 spinlock_lock(&instance->input_lock);
169
170 /* we need pointers to volatile variables */
171 volatile char *buf_ptr = (volatile char *)
172 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
173 volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
174 volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
175
176 while (*in_rdptr_ptr != *in_wrptr_ptr) {
177 buf_ptr = (volatile char *)
178 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
179 char c = *buf_ptr;
180 *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
181
182 indev_push_character(instance->srlnin, c);
183 }
184
185 spinlock_unlock(&instance->input_lock);
186}
187
188/**
189 * Polling thread function.
190 */
191static void ksgcnpoll(void *instance) {
192 while (true) {
193 if (!silent)
194 sgcn_poll(instance);
195
196 thread_usleep(POLL_INTERVAL);
197 }
198}
199
200/**
201 * Initializes the starting address of the SGCN buffer.
202 *
203 * The offset of the SGCN buffer within SRAM is obtained from the
204 * SRAM table of contents. The table of contents contains
205 * information about several buffers, among which there is an OBP
206 * console buffer - this one will be used as the SGCN buffer.
207 *
208 * This function also writes the offset of the SGCN buffer within SRAM
209 * under the sram.buffer.offset sysinfo key.
210 */
211static void sgcn_init(void)
212{
213 if (instance)
214 return;
215
216 instance = malloc(sizeof(sgcn_instance_t), FRAME_ATOMIC);
217
218 if (instance) {
219 instance->thread = thread_create(ksgcnpoll, instance, TASK, 0,
220 "ksgcnpoll", true);
221
222 if (!instance->thread) {
223 free(instance);
224 instance = NULL;
225 return;
226 }
227
228 init_sram_begin();
229
230 ASSERT(str_cmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
231
232 /* Lookup TOC entry with the correct key */
233 uint32_t i;
234 for (i = 0; i < MAX_TOC_ENTRIES; i++) {
235 if (str_cmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
236 break;
237 }
238 ASSERT(i < MAX_TOC_ENTRIES);
239
240 instance->buffer_begin =
241 instance->sram_begin + SRAM_TOC->keys[i].offset;
242
243 sysinfo_set_item_val("sram.buffer.offset", NULL,
244 SRAM_TOC->keys[i].offset);
245
246 instance->srlnin = NULL;
247 }
248}
249
250/**
251 * Writes a single character to the SGCN (circular) output buffer
252 * and updates the output write pointer so that SGCN gets to know
253 * that the character has been written.
254 */
255static void sgcn_do_putchar(const char c)
256{
257 uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
258 uint32_t end = SGCN_BUFFER_HEADER->out_end;
259 uint32_t size = end - begin;
260
261 /* We need pointers to volatile variables */
262 volatile char *buf_ptr = (volatile char *)
263 SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
264 volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
265 volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
266
267 /*
268 * Write the character and increment the write pointer modulo the
269 * output buffer size. Note that if we are to rewrite a character
270 * which has not been read by the SGCN controller yet (i.e. the output
271 * buffer is full), we need to wait until the controller reads some more
272 * characters. We wait actively, which means that all threads waiting
273 * for the lock are blocked. However, this situation is
274 * 1) rare - the output buffer is big, so filling the whole
275 * output buffer is improbable
276 * 2) short-lasting - it will take the controller only a fraction
277 * of millisecond to pick the unread characters up
278 * 3) not serious - the blocked threads are those that print something
279 * to user console, which is not a time-critical operation
280 */
281 uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
282 while (*out_rdptr_ptr == new_wrptr);
283
284 *buf_ptr = c;
285 *out_wrptr_ptr = new_wrptr;
286}
287
288/**
289 * SGCN output operation. Prints a single character to the SGCN. Newline
290 * character is converted to CRLF.
291 */
292static void sgcn_putchar(outdev_t *dev, const wchar_t ch, bool silent)
293{
294 if (!silent) {
295 spinlock_lock(&instance->output_lock);
296
297 if (ascii_check(ch)) {
298 if (ch == '\n')
299 sgcn_do_putchar('\r');
300 sgcn_do_putchar(ch);
301 } else
302 sgcn_do_putchar(U_SPECIAL);
303
304 spinlock_unlock(&instance->output_lock);
305 }
306}
307
308/**
309 * A public function which initializes input from the Serengeti console.
310 */
311sgcn_instance_t *sgcnin_init(void)
312{
313 sgcn_init();
314 return instance;
315}
316
317void sgcnin_wire(sgcn_instance_t *instance, indev_t *srlnin)
318{
319 ASSERT(instance);
320 ASSERT(srlnin);
321
322 instance->srlnin = srlnin;
323 thread_ready(instance->thread);
324
325 sysinfo_set_item_val("kbd", NULL, true);
326}
327
328/**
329 * A public function which initializes output to the Serengeti console.
330 */
331outdev_t *sgcnout_init(void)
332{
333 sgcn_init();
334 if (!instance)
335 return NULL;
336
337 outdev_t *sgcndev = malloc(sizeof(outdev_t), FRAME_ATOMIC);
338 if (!sgcndev)
339 return NULL;
340
341 outdev_initialize("sgcndev", sgcndev, &sgcndev_ops);
342 sgcndev->data = instance;
343
344 if (!fb_exported) {
345 /*
346 * This is the necessary evil until the userspace driver is entirely
347 * self-sufficient.
348 */
349 sysinfo_set_item_val("fb.kind", NULL, 4);
350
351 fb_exported = true;
352 }
353
354 return sgcndev;
355}
356
357/** @}
358 */
Note: See TracBrowser for help on using the repository browser.