Subversion Repositories HelenOS

Rev

Rev 4071 | Rev 4203 | 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 char, 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. If the line
  268.  * feed character is written ('\n'), the carriage return character ('\r') is
  269.  * written straight away.
  270.  */
  271. static void sgcn_putchar(outdev_t *od, const char c, bool silent)
  272. {
  273.     if (!silent) {
  274.         spinlock_lock(&sgcn_output_lock);
  275.        
  276.         sgcn_do_putchar(c);
  277.         if (c == '\n')
  278.             sgcn_do_putchar('\r');
  279.        
  280.         spinlock_unlock(&sgcn_output_lock);
  281.     }
  282. }
  283.  
  284. /**
  285.  * Grabs the input for kernel.
  286.  */
  287. void sgcn_grab(void)
  288. {
  289.     kbd_disabled = true;
  290. }
  291.  
  292. /**
  293.  * Releases the input so that userspace can use it.
  294.  */
  295. void sgcn_release(void)
  296. {
  297.     kbd_disabled = true;
  298. }
  299.  
  300. /**
  301.  * Function regularly called by the keyboard polling thread. Finds out whether
  302.  * there are some unread characters in the input queue. If so, it picks them up
  303.  * and sends them to the upper layers of HelenOS.
  304.  */
  305. static void sgcn_poll()
  306. {
  307.     uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
  308.     uint32_t end = SGCN_BUFFER_HEADER->in_end;
  309.     uint32_t size = end - begin;
  310.  
  311.     if (kbd_disabled)
  312.         return;
  313.  
  314.     spinlock_lock(&sgcn_input_lock);
  315.    
  316.     /* we need pointers to volatile variables */
  317.     volatile char *buf_ptr = (volatile char *)
  318.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  319.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  320.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  321.    
  322.     while (*in_rdptr_ptr != *in_wrptr_ptr) {
  323.        
  324.         buf_ptr = (volatile char *)
  325.             SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  326.         char c = *buf_ptr;
  327.         *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
  328.            
  329.         indev_push_character(&sgcnin, c);  
  330.     }  
  331.  
  332.     spinlock_unlock(&sgcn_input_lock);
  333. }
  334.  
  335. /**
  336.  * Polling thread function.
  337.  */
  338. static void kkbdpoll(void *arg) {
  339.     while (1) {
  340.         if (!silent) {
  341.             sgcn_poll();
  342.         }
  343.         thread_usleep(POLL_INTERVAL);
  344.     }
  345. }
  346.  
  347. /**
  348.  * A public function which initializes input from the Serengeti console.
  349.  */
  350. indev_t *sgcnin_init(void)
  351. {
  352.     sgcn_buffer_begin_init();
  353.  
  354.     kbd_type = KBD_SGCN;
  355.  
  356.     sysinfo_set_item_val("kbd", NULL, true);
  357.     sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
  358.  
  359.     thread_t *t = thread_create(kkbdpoll, NULL, TASK, 0, "kkbdpoll", true);
  360.     if (!t)
  361.         panic("Cannot create kkbdpoll.");
  362.     thread_ready(t);
  363.    
  364.     indev_initialize("sgcnin", &sgcnin, &sgcnin_ops);
  365.  
  366.     return &sgcnin;
  367. }
  368.  
  369. /**
  370.  * A public function which initializes output to the Serengeti console.
  371.  */
  372. void sgcnout_init(void)
  373. {
  374.     sgcn_buffer_begin_init();
  375.  
  376.     sysinfo_set_item_val("fb.kind", NULL, 4);
  377.  
  378.     outdev_initialize("sgcnout", &sgcnout, &sgcnout_ops);  
  379.     stdout = &sgcnout;
  380. }
  381.  
  382. /** @}
  383.  */
  384.