Subversion Repositories HelenOS

Rev

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