Subversion Repositories HelenOS

Rev

Rev 4156 | 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_putchar(outdev_t *, const wchar_t, bool);
  134.  
  135. /** SGCN output device operations */
  136. static outdev_operations_t sgcnout_ops = {
  137.     .write = sgcn_putchar
  138. };
  139.  
  140. /** SGCN input device operations */
  141. static indev_operations_t sgcnin_ops = {
  142.     .poll = NULL
  143. };
  144.  
  145. static indev_t sgcnin;      /**< SGCN input device. */
  146. static outdev_t sgcnout;    /**< SGCN output device. */
  147.  
  148. /**
  149.  * Set some sysinfo values (SRAM address and SRAM size).
  150.  */
  151. static void register_sram(uintptr_t sram_begin_physical)
  152. {
  153.     sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
  154.     sysinfo_set_item_val("sram.address.physical", NULL,
  155.         sram_begin_physical);
  156. }
  157.  
  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
  165.  * address.
  166.  */
  167. static void init_sram_begin(void)
  168. {
  169.     ofw_tree_node_t *chosen;
  170.     ofw_tree_property_t *iosram_toc;
  171.     uintptr_t sram_begin_physical;
  172.  
  173.     chosen = ofw_tree_lookup("/chosen");
  174.     if (!chosen)
  175.         panic("Cannot find '/chosen'.");
  176.  
  177.     iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
  178.     if (!iosram_toc)
  179.         panic("Cannot find property 'iosram-toc'.");
  180.     if (!iosram_toc->value)
  181.         panic("Cannot find SRAM TOC.");
  182.  
  183.     sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
  184.         + *((uint32_t *) iosram_toc->value);
  185.     sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
  186.    
  187.     register_sram(sram_begin_physical);
  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.
  197.  *
  198.  * This function also writes the offset of the SGCN buffer within SRAM
  199.  * under the sram.buffer.offset sysinfo key.
  200.  */
  201. static void sgcn_buffer_begin_init(void)
  202. {
  203.     static bool initialized;
  204.    
  205.     if (initialized)
  206.         return;
  207.  
  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;
  221.    
  222.     sysinfo_set_item_val("sram.buffer.offset", NULL,
  223.         SRAM_TOC->keys[i].offset);
  224.    
  225.     initialized = true;
  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 *)
  241.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
  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. Newline
  268.  * character is converted to CRLF.
  269.  */
  270. static void sgcn_putchar(outdev_t *od, const wchar_t ch, bool silent)
  271. {
  272.     if (!silent) {
  273.         spinlock_lock(&sgcn_output_lock);
  274.        
  275.         if (ascii_check(ch)) {
  276.             if (ch == '\n')
  277.                 sgcn_do_putchar('\r');
  278.             sgcn_do_putchar(ch);
  279.         } else
  280.             sgcn_do_putchar(invalch);
  281.        
  282.         spinlock_unlock(&sgcn_output_lock);
  283.     }
  284. }
  285.  
  286. /**
  287.  * Grabs the input for kernel.
  288.  */
  289. void sgcn_grab(void)
  290. {
  291.     kbd_disabled = true;
  292. }
  293.  
  294. /**
  295.  * Releases the input so that userspace can use it.
  296.  */
  297. void sgcn_release(void)
  298. {
  299.     kbd_disabled = true;
  300. }
  301.  
  302. /**
  303.  * Function regularly called by the keyboard polling thread. Finds out whether
  304.  * there are some unread characters in the input queue. If so, it picks them up
  305.  * and sends them to the upper layers of HelenOS.
  306.  */
  307. static void sgcn_poll()
  308. {
  309.     uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
  310.     uint32_t end = SGCN_BUFFER_HEADER->in_end;
  311.     uint32_t size = end - begin;
  312.  
  313.     if (kbd_disabled)
  314.         return;
  315.  
  316.     spinlock_lock(&sgcn_input_lock);
  317.    
  318.     /* we need pointers to volatile variables */
  319.     volatile char *buf_ptr = (volatile char *)
  320.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  321.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  322.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  323.    
  324.     while (*in_rdptr_ptr != *in_wrptr_ptr) {
  325.        
  326.         buf_ptr = (volatile char *)
  327.             SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  328.         char c = *buf_ptr;
  329.         *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
  330.            
  331.         indev_push_character(&sgcnin, c);  
  332.     }  
  333.  
  334.     spinlock_unlock(&sgcn_input_lock);
  335. }
  336.  
  337. /**
  338.  * Polling thread function.
  339.  */
  340. static void kkbdpoll(void *arg) {
  341.     while (1) {
  342.         if (!silent) {
  343.             sgcn_poll();
  344.         }
  345.         thread_usleep(POLL_INTERVAL);
  346.     }
  347. }
  348.  
  349. /**
  350.  * A public function which initializes input from the Serengeti console.
  351.  */
  352. indev_t *sgcnin_init(void)
  353. {
  354.     sgcn_buffer_begin_init();
  355.  
  356.     kbd_type = KBD_SGCN;
  357.  
  358.     sysinfo_set_item_val("kbd", NULL, true);
  359.     sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
  360.  
  361.     thread_t *t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true);
  362.     if (!t)
  363.         panic("Cannot create kkbdpoll.");
  364.     thread_ready(t);
  365.    
  366.     indev_initialize("sgcnin", &sgcnin, &sgcnin_ops);
  367.  
  368.     return &sgcnin;
  369. }
  370.  
  371. /**
  372.  * A public function which initializes output to the Serengeti console.
  373.  */
  374. void sgcnout_init(void)
  375. {
  376.     sgcn_buffer_begin_init();
  377.  
  378.     sysinfo_set_item_val("fb.kind", NULL, 4);
  379.  
  380.     outdev_initialize("sgcnout", &sgcnout, &sgcnout_ops);  
  381.     stdout = &sgcnout;
  382. }
  383.  
  384. /** @}
  385.  */
  386.