Subversion Repositories HelenOS

Rev

Rev 3502 | Rev 3618 | 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/drivers/sgcn.h>
  38. #include <genarch/ofw/ofw_tree.h>
  39. #include <debug.h>
  40. #include <func.h>
  41. #include <print.h>
  42. #include <mm/page.h>
  43. #include <console/chardev.h>
  44. #include <console/console.h>
  45. #include <synch/spinlock.h>
  46.  
  47. /*
  48.  * Physical address at which the SBBC starts. This value has been obtained
  49.  * by inspecting (using Simics) memory accesses made by OBP. It is valid
  50.  * for the Simics-simulated Serengeti machine. The author of this code is
  51.  * not sure whether this value is valid generally.
  52.  */
  53. #define SBBC_START      0x63000000000
  54.  
  55. /* offset of SRAM within the SBBC memory */
  56. #define SBBC_SRAM_OFFSET    0x900000
  57.  
  58. /* magic string contained at the beginning of SRAM */
  59. #define SRAM_TOC_MAGIC      "TOCSRAM"
  60.  
  61. /*
  62.  * Key into the SRAM table of contents which identifies the entry
  63.  * describing the OBP console buffer. It is worth mentioning
  64.  * that the OBP console buffer is not the only console buffer
  65.  * which can be used. It is, however, used because when the kernel
  66.  * is running, the OBP buffer is not used by OBP any more but OBP
  67.  * has already made neccessary arangements so that the output will
  68.  * be read from the OBP buffer and input will go to the OBP buffer.
  69.  * Therefore HelenOS needs to make no such arrangements any more.
  70.  */
  71. #define CONSOLE_KEY     "OBPCONS"
  72.  
  73. /* magic string contained at the beginning of the console buffer */
  74. #define SGCN_BUFFER_MAGIC   "CON"
  75.  
  76.  
  77. /*
  78.  * Returns a pointer to the object of a given type which is placed at the given
  79.  * offset from the SRAM beginning.
  80.  */
  81. #define SRAM(type, offset)  ((type *) (sram_begin + (offset)))
  82.  
  83. /* Returns a pointer to the SRAM table of contents. */
  84. #define SRAM_TOC        (SRAM(iosram_toc_t, 0))
  85.  
  86. /*
  87.  * Returns a pointer to the object of a given type which is placed at the given
  88.  * offset from the console buffer beginning.
  89.  */
  90. #define SGCN_BUFFER(type, offset) \
  91.                 ((type *) (sgcn_buffer_begin + (offset)))
  92.  
  93. /* Returns a pointer to the console buffer header. */
  94. #define SGCN_BUFFER_HEADER  (SGCN_BUFFER(sgcn_buffer_header_t, 0))
  95.  
  96.  
  97. /* starting address of SRAM, will be set by the init_sram_begin function */
  98. static uintptr_t sram_begin;
  99.  
  100. /*
  101.  * starting address of the SGCN buffer, will be set by the
  102.  * init_sgcn_buffer_begin function
  103.  */
  104. static uintptr_t sgcn_buffer_begin;
  105.  
  106.  
  107. /*
  108.  * Ensures that writing to the buffer and consequent update of the write pointer
  109.  * are together one atomic operation.
  110.  */
  111. SPINLOCK_INITIALIZE(sgcn_output_lock);
  112.  
  113. /*
  114.  * Ensures that reading from the buffer and consequent update of the read
  115.  * pointer are together one atomic operation.
  116.  */
  117. SPINLOCK_INITIALIZE(sgcn_input_lock);
  118.  
  119.  
  120. /* functions referenced from definitions of I/O operations structures */
  121. static void sgcn_noop(chardev_t *);
  122. static void sgcn_putchar(chardev_t *, const char);
  123. static char sgcn_key_read(chardev_t *);
  124.  
  125. /** character device output operations */
  126. static chardev_operations_t sgcn_output_ops = {
  127.     .suspend = sgcn_noop,
  128.     .resume = sgcn_noop,
  129.     .write = sgcn_putchar,
  130.     .read = NULL
  131. };
  132.  
  133. /** character device input operations */
  134. static chardev_operations_t sgcn_input_ops = {
  135.     .suspend = sgcn_noop,
  136.     .resume = sgcn_noop,
  137.     .read = sgcn_key_read
  138. };
  139.  
  140.  
  141. /** SGCN character output device */
  142. chardev_t sgcn_stdout;
  143.  
  144. /** SGCN character input device */
  145. chardev_t sgcn_input;
  146.  
  147.  
  148. /**
  149.  * Initializes the starting address of SRAM.
  150.  *
  151.  * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
  152.  * physical memory, where C is the value read from the "iosram-toc"
  153.  * property of the "/chosen" OBP node. The sram_begin variable will
  154.  * be set to the virtual address which maps to the SRAM physical
  155.  * address.
  156.  */
  157. static void init_sram_begin(void)
  158. {
  159.     ofw_tree_node_t *chosen;
  160.     ofw_tree_property_t *iosram_toc;
  161.  
  162.     chosen = ofw_tree_lookup("/chosen");
  163.     if (!chosen)
  164.         panic("Can't find /chosen.\n");
  165.  
  166.     iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
  167.     if (!iosram_toc)
  168.         panic("Can't find property \"iosram-toc\".\n");
  169.     if (!iosram_toc->value)
  170.         panic("Can't find SRAM TOC.\n");
  171.  
  172.     sram_begin = hw_map(
  173.         SBBC_START + SBBC_SRAM_OFFSET
  174.         + *((uint32_t *) iosram_toc->value),
  175.         128 * 1024);
  176. }
  177.  
  178. /**
  179.  * Initializes the starting address of the SGCN buffer.
  180.  *
  181.  * The offset of the SGCN buffer within SRAM is obtained from the
  182.  * SRAM table of contents. The table of contents contains
  183.  * information about several buffers, among which there is an OBP
  184.  * console buffer - this one will be used as the SGCN buffer.
  185.  */
  186. static void sgcn_buffer_begin_init(void)
  187. {
  188.     init_sram_begin();
  189.        
  190.     ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
  191.    
  192.     /* lookup TOC entry with the correct key */
  193.     uint32_t i;
  194.     for (i = 0; i < MAX_TOC_ENTRIES; i++) {
  195.         if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
  196.             break;
  197.     }
  198.     ASSERT(i < MAX_TOC_ENTRIES);
  199.    
  200.     sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
  201. }
  202.  
  203. /**
  204.  * Default suspend/resume operation for the input device.
  205.  */
  206. static void sgcn_noop(chardev_t *d)
  207. {
  208. }
  209.  
  210. /**
  211.  * Writes a single character to the SGCN (circular) output buffer
  212.  * and updates the output write pointer so that SGCN gets to know
  213.  * that the character has been written.
  214.  */
  215. static void sgcn_do_putchar(const char c)
  216. {
  217.     uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
  218.     uint32_t end = SGCN_BUFFER_HEADER->out_end;
  219.     uint32_t size = end - begin;
  220.    
  221.     /* we need pointers to volatile variables */
  222.     volatile char *buf_ptr = (volatile char *)
  223.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
  224.     volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
  225.     volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
  226.  
  227.     /*
  228.      * Write the character and increment the write pointer modulo the
  229.      * output buffer size. Note that if we are to rewrite a character
  230.      * which has not been read by the SGCN controller yet (i.e. the output
  231.      * buffer is full), we need to wait until the controller reads some more
  232.      * characters. We wait actively, which means that all threads waiting
  233.      * for the lock are blocked. However, this situation is
  234.      *   1) rare - the output buffer is big, so filling the whole
  235.      *             output buffer is improbable
  236.      *   2) short-lasting - it will take the controller only a fraction
  237.      *             of millisecond to pick the unread characters up
  238.      *   3) not serious - the blocked threads are those that print something
  239.      *             to user console, which is not a time-critical operation
  240.      */
  241.     uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
  242.     while (*out_rdptr_ptr == new_wrptr)
  243.         ;
  244.     *buf_ptr = c;
  245.     *out_wrptr_ptr = new_wrptr;
  246. }
  247.  
  248. /**
  249.  * SGCN output operation. Prints a single character to the SGCN. If the line
  250.  * feed character is written ('\n'), the carriage return character ('\r') is
  251.  * written straight away.
  252.  */
  253. static void sgcn_putchar(struct chardev * cd, const char c)
  254. {
  255.     spinlock_lock(&sgcn_output_lock);
  256.    
  257.     sgcn_do_putchar(c);
  258.     if (c == '\n') {
  259.         sgcn_do_putchar('\r');
  260.     }
  261.    
  262.     spinlock_unlock(&sgcn_output_lock);
  263. }
  264.  
  265. /**
  266.  * Called when actively reading the character. Not implemented yet.
  267.  */
  268. static char sgcn_key_read(chardev_t *d)
  269. {
  270.     return (char) 0;
  271. }
  272.  
  273. /**
  274.  * Function regularly called by the keyboard polling thread. Finds out whether
  275.  * there are some unread characters in the input queue. If so, it picks them up
  276.  * and sends them to the upper layers of HelenOS.
  277.  */
  278. void sgcn_poll(void)
  279. {
  280.     spinlock_lock(&sgcn_input_lock);
  281.    
  282.     char c;
  283.     uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
  284.     uint32_t end = SGCN_BUFFER_HEADER->in_end;
  285.     uint32_t size = end - begin;
  286.    
  287.     /* we need pointers to volatile variables */
  288.     volatile char *buf_ptr = (volatile char *)
  289.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  290.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  291.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  292.    
  293.     while (*in_rdptr_ptr != *in_wrptr_ptr) {
  294.         c = *buf_ptr;
  295.         *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
  296.         buf_ptr = (volatile char *)
  297.             SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  298.         chardev_push_character(&sgcn_input, c);
  299.         if (c == '\r')
  300.             chardev_push_character(&sgcn_input, '\n');
  301.     }
  302.    
  303.     spinlock_unlock(&sgcn_input_lock);
  304. }
  305.  
  306. /**
  307.  * A public function which initializes I/O from/to Serengeti console
  308.  * and sets it as a default input/output.
  309.  */
  310. void sgcn_init(void)
  311. {
  312.     sgcn_buffer_begin_init();
  313.    
  314.     chardev_initialize("sgcn_output", &sgcn_stdout, &sgcn_output_ops);
  315.     stdout = &sgcn_stdout;
  316.    
  317.     chardev_initialize("sgcn_input", &sgcn_input, &sgcn_input_ops);
  318.     stdin = &sgcn_input;
  319. }
  320.  
  321. /** @}
  322.  */