Subversion Repositories HelenOS

Rev

Rev 4070 | Rev 4202 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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 */
  105. extern kbd_type_t kbd_type;
  106.  
  107. /** starting address of SRAM, will be set by the init_sram_begin function */
  108. static 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.  */
  114. static uintptr_t sgcn_buffer_begin;
  115.  
  116. /* true iff the kernel driver should ignore pressed keys */
  117. static 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.  */
  123. SPINLOCK_INITIALIZE(sgcn_output_lock);
  124.  
  125. /*
  126.  * Prevents the input buffer read/write pointers from getting to inconsistent
  127.  * state.
  128.  */
  129. SPINLOCK_INITIALIZE(sgcn_input_lock);
  130.  
  131.  
  132. /* functions referenced from definitions of I/O operations structures */
  133. static void sgcn_noop(chardev_t *);
  134. static void sgcn_putchar(chardev_t *, const char, bool);
  135. static char sgcn_key_read(chardev_t *);
  136.  
  137. /** character device operations */
  138. static 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 */
  146. chardev_t sgcn_io;
  147.  
  148. /** Address of the chardev, which is connected to SGCN. */
  149. static chardev_t *sgcnout;
  150.  
  151. /**
  152.  * Set some sysinfo values (SRAM address and SRAM size).
  153.  */
  154. static 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.  */
  170. static 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.  */
  204. static 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.  */
  227. static 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.  */
  236. static 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.  */
  274. static 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.  */
  290. static char sgcn_key_read(chardev_t *d)
  291. {
  292.     return (char) 0;
  293. }
  294.  
  295. /**
  296.  * Grabs the input for kernel.
  297.  */
  298. void sgcn_grab(void)
  299. {
  300.     kbd_disabled = true;
  301. }
  302.  
  303. /**
  304.  * Releases the input so that userspace can use it.
  305.  */
  306. void 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.  */
  316. static 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.  */
  350. static 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.  */
  363. void 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.  */
  386.