Subversion Repositories HelenOS

Rev

Rev 4341 | Rev 4344 | 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 <arch/drivers/kbd.h>
  39. #include <genarch/ofw/ofw_tree.h>
  40. #include <debug.h>
  41. #include <func.h>
  42. #include <print.h>
  43. #include <mm/page.h>
  44. #include <ipc/irq.h>
  45. #include <ddi/ddi.h>
  46. #include <ddi/device.h>
  47. #include <console/chardev.h>
  48. #include <console/console.h>
  49. #include <ddi/device.h>
  50. #include <sysinfo/sysinfo.h>
  51. #include <synch/spinlock.h>
  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 neccessary arangements 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.  * The driver is polling based, but in order to notify the userspace
  87.  * of a key being pressed, we need to supply the interface with some
  88.  * interrupt number. The interrupt number can be arbitrary as it it
  89.  * will never be used for identifying HW interrupts, but only in
  90.  * notifying the userspace.
  91.  */
  92. #define FICTIONAL_INR       1
  93.  
  94.  
  95. /*
  96.  * Returns a pointer to the object of a given type which is placed at the given
  97.  * offset from the SRAM beginning.
  98.  */
  99. #define SRAM(type, offset)  ((type *) (sram_begin + (offset)))
  100.  
  101. /* Returns a pointer to the SRAM table of contents. */
  102. #define SRAM_TOC        (SRAM(iosram_toc_t, 0))
  103.  
  104. /*
  105.  * Returns a pointer to the object of a given type which is placed at the given
  106.  * offset from the console buffer beginning.
  107.  */
  108. #define SGCN_BUFFER(type, offset) \
  109.                 ((type *) (sgcn_buffer_begin + (offset)))
  110.  
  111. /** Returns a pointer to the console buffer header. */
  112. #define SGCN_BUFFER_HEADER  (SGCN_BUFFER(sgcn_buffer_header_t, 0))
  113.  
  114. /** defined in drivers/kbd.c */
  115. extern kbd_type_t kbd_type;
  116.  
  117. /** starting address of SRAM, will be set by the init_sram_begin function */
  118. static uintptr_t sram_begin;
  119.  
  120. /**
  121.  * starting address of the SGCN buffer, will be set by the
  122.  * init_sgcn_buffer_begin function
  123.  */
  124. static uintptr_t sgcn_buffer_begin;
  125.  
  126. /**
  127.  * SGCN IRQ structure. So far used only for notifying the userspace of the
  128.  * key being pressed, not for kernel being informed about keyboard interrupts.
  129.  */
  130. static irq_t sgcn_irq;
  131.  
  132. // TODO think of a way how to synchronize accesses to SGCN buffer between the kernel and the userspace
  133.  
  134. /*
  135.  * Ensures that writing to the buffer and consequent update of the write pointer
  136.  * are together one atomic operation.
  137.  */
  138. SPINLOCK_INITIALIZE(sgcn_output_lock);
  139.  
  140. /*
  141.  * Prevents the input buffer read/write pointers from getting to inconsistent
  142.  * state.
  143.  */
  144. SPINLOCK_INITIALIZE(sgcn_input_lock);
  145.  
  146.  
  147. /* functions referenced from definitions of I/O operations structures */
  148. static void sgcn_noop(chardev_t *);
  149. static void sgcn_putchar(chardev_t *, const char, bool);
  150. static char sgcn_key_read(chardev_t *);
  151.  
  152. /** character device operations */
  153. static chardev_operations_t sgcn_ops = {
  154.     .suspend = sgcn_noop,
  155.     .resume = sgcn_noop,
  156.     .read = sgcn_key_read,
  157.     .write = sgcn_putchar
  158. };
  159.  
  160. /** SGCN character device */
  161. chardev_t sgcn_io;
  162.  
  163. /**
  164.  * Set some sysinfo values (SRAM address and SRAM size).
  165.  */
  166. static void register_sram(uintptr_t sram_begin_physical)
  167. {
  168.     sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
  169.     sysinfo_set_item_val("sram.address.physical", NULL,
  170.         sram_begin_physical);
  171. }
  172.  
  173. /**
  174.  * Initializes the starting address of SRAM.
  175.  *
  176.  * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
  177.  * physical memory, where C is the value read from the "iosram-toc"
  178.  * property of the "/chosen" OBP node. The sram_begin variable will
  179.  * be set to the virtual address which maps to the SRAM physical
  180.  * address.
  181.  *
  182.  * It also registers the physical area of SRAM and sets some sysinfo
  183.  * values (SRAM address and SRAM size).
  184.  */
  185. static void init_sram_begin(void)
  186. {
  187.     ofw_tree_node_t *chosen;
  188.     ofw_tree_property_t *iosram_toc;
  189.     uintptr_t sram_begin_physical;
  190.  
  191.     chosen = ofw_tree_lookup("/chosen");
  192.     if (!chosen)
  193.         panic("Cannot find '/chosen'.");
  194.  
  195.     iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
  196.     if (!iosram_toc)
  197.         panic("Cannot find property 'iosram-toc'.");
  198.     if (!iosram_toc->value)
  199.         panic("Cannot find SRAM TOC.");
  200.  
  201.     sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
  202.         + *((uint32_t *) iosram_toc->value);
  203.     sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
  204.    
  205.     register_sram(sram_begin_physical);
  206. }
  207.  
  208. /**
  209.  * Initializes the starting address of the SGCN buffer.
  210.  *
  211.  * The offset of the SGCN buffer within SRAM is obtained from the
  212.  * SRAM table of contents. The table of contents contains
  213.  * information about several buffers, among which there is an OBP
  214.  * console buffer - this one will be used as the SGCN buffer.
  215.  *
  216.  * This function also writes the offset of the SGCN buffer within SRAM
  217.  * under the sram.buffer.offset sysinfo key.
  218.  */
  219. static void sgcn_buffer_begin_init(void)
  220. {
  221.     init_sram_begin();
  222.        
  223.     ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
  224.    
  225.     /* lookup TOC entry with the correct key */
  226.     uint32_t i;
  227.     for (i = 0; i < MAX_TOC_ENTRIES; i++) {
  228.         if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
  229.             break;
  230.     }
  231.     ASSERT(i < MAX_TOC_ENTRIES);
  232.    
  233.     sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
  234.    
  235.     sysinfo_set_item_val("sram.buffer.offset", NULL,
  236.         SRAM_TOC->keys[i].offset);
  237. }
  238.  
  239. /**
  240.  * Default suspend/resume operation for the input device.
  241.  */
  242. static void sgcn_noop(chardev_t *d)
  243. {
  244. }
  245.  
  246. /**
  247.  * Writes a single character to the SGCN (circular) output buffer
  248.  * and updates the output write pointer so that SGCN gets to know
  249.  * that the character has been written.
  250.  */
  251. static void sgcn_do_putchar(const char c)
  252. {
  253.     uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
  254.     uint32_t end = SGCN_BUFFER_HEADER->out_end;
  255.     uint32_t size = end - begin;
  256.    
  257.     /* we need pointers to volatile variables */
  258.     volatile char *buf_ptr = (volatile char *)
  259.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
  260.     volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
  261.     volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
  262.  
  263.     /*
  264.      * Write the character and increment the write pointer modulo the
  265.      * output buffer size. Note that if we are to rewrite a character
  266.      * which has not been read by the SGCN controller yet (i.e. the output
  267.      * buffer is full), we need to wait until the controller reads some more
  268.      * characters. We wait actively, which means that all threads waiting
  269.      * for the lock are blocked. However, this situation is
  270.      *   1) rare - the output buffer is big, so filling the whole
  271.      *             output buffer is improbable
  272.      *   2) short-lasting - it will take the controller only a fraction
  273.      *             of millisecond to pick the unread characters up
  274.      *   3) not serious - the blocked threads are those that print something
  275.      *             to user console, which is not a time-critical operation
  276.      */
  277.     uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
  278.     while (*out_rdptr_ptr == new_wrptr)
  279.         ;
  280.     *buf_ptr = c;
  281.     *out_wrptr_ptr = new_wrptr;
  282. }
  283.  
  284. /**
  285.  * SGCN output operation. Prints a single character to the SGCN. If the line
  286.  * feed character is written ('\n'), the carriage return character ('\r') is
  287.  * written straight away.
  288.  */
  289. static void sgcn_putchar(struct chardev * cd, const char c, bool silent)
  290. {
  291.     if (!silent) {
  292.         spinlock_lock(&sgcn_output_lock);
  293.        
  294.         sgcn_do_putchar(c);
  295.         if (c == '\n')
  296.             sgcn_do_putchar('\r');
  297.        
  298.         spinlock_unlock(&sgcn_output_lock);
  299.     }
  300. }
  301.  
  302. /**
  303.  * Called when actively reading the character. Not implemented yet.
  304.  */
  305. static char sgcn_key_read(chardev_t *d)
  306. {
  307.     return (char) 0;
  308. }
  309.  
  310. /**
  311.  * The driver works in polled mode, so no interrupt should be handled by it.
  312.  */
  313. static irq_ownership_t sgcn_claim(void *instance)
  314. {
  315.     return IRQ_DECLINE;
  316. }
  317.  
  318. /**
  319.  * The driver works in polled mode, so no interrupt should be handled by it.
  320.  */
  321. static void sgcn_irq_handler(irq_t *irq)
  322. {
  323.     panic("Not yet implemented, SGCN works in polled mode.");
  324. }
  325.  
  326. /**
  327.  * Grabs the input for kernel.
  328.  */
  329. void sgcn_grab(void)
  330. {
  331.     ipl_t ipl = interrupts_disable();
  332.    
  333.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  334.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  335.    
  336.     /* skip all the user typed before the grab and hasn't been processed */
  337.     spinlock_lock(&sgcn_input_lock);
  338.     *in_rdptr_ptr = *in_wrptr_ptr;
  339.     spinlock_unlock(&sgcn_input_lock);
  340.  
  341.     spinlock_lock(&sgcn_irq.lock);
  342.     sgcn_irq.notif_cfg.notify = false;
  343.     spinlock_unlock(&sgcn_irq.lock);
  344.    
  345.     interrupts_restore(ipl);
  346. }
  347.  
  348. /**
  349.  * Releases the input so that userspace can use it.
  350.  */
  351. void sgcn_release(void)
  352. {
  353.     ipl_t ipl = interrupts_disable();
  354.     spinlock_lock(&sgcn_irq.lock);
  355.     if (sgcn_irq.notif_cfg.answerbox)
  356.         sgcn_irq.notif_cfg.notify = true;
  357.     spinlock_unlock(&sgcn_irq.lock);
  358.     interrupts_restore(ipl);
  359. }
  360.  
  361. /**
  362.  * Function regularly called by the keyboard polling thread. Finds out whether
  363.  * there are some unread characters in the input queue. If so, it picks them up
  364.  * and sends them to the upper layers of HelenOS.
  365.  */
  366. void sgcn_poll(void)
  367. {
  368.     uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
  369.     uint32_t end = SGCN_BUFFER_HEADER->in_end;
  370.     uint32_t size = end - begin;
  371.    
  372.     spinlock_lock(&sgcn_input_lock);
  373.    
  374.     ipl_t ipl = interrupts_disable();
  375.     spinlock_lock(&sgcn_irq.lock);
  376.    
  377.     /* we need pointers to volatile variables */
  378.     volatile char *buf_ptr = (volatile char *)
  379.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  380.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  381.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  382.    
  383.     if (*in_rdptr_ptr != *in_wrptr_ptr) {
  384.         if (sgcn_irq.notif_cfg.notify && sgcn_irq.notif_cfg.answerbox) {
  385.             ipc_irq_send_notif(&sgcn_irq);
  386.             spinlock_unlock(&sgcn_irq.lock);
  387.             interrupts_restore(ipl);
  388.             spinlock_unlock(&sgcn_input_lock);
  389.             return;
  390.         }
  391.     }
  392.    
  393.     spinlock_unlock(&sgcn_irq.lock);
  394.     interrupts_restore(ipl);   
  395.  
  396.     while (*in_rdptr_ptr != *in_wrptr_ptr) {
  397.        
  398.         buf_ptr = (volatile char *)
  399.             SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  400.         char c = *buf_ptr;
  401.         *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
  402.            
  403.         if (c == '\r') {
  404.             c = '\n';
  405.         }
  406.         chardev_push_character(&sgcn_io, c);   
  407.     }  
  408.    
  409.     spinlock_unlock(&sgcn_input_lock);
  410. }
  411.  
  412. /**
  413.  * A public function which initializes I/O from/to Serengeti console
  414.  * and sets it as a default input/output.
  415.  */
  416. void sgcn_init(void)
  417. {
  418.     sgcn_buffer_begin_init();
  419.  
  420.     kbd_type = KBD_SGCN;
  421.  
  422.     devno_t devno = device_assign_devno();
  423.     irq_initialize(&sgcn_irq);
  424.     sgcn_irq.devno = devno;
  425.     sgcn_irq.inr = FICTIONAL_INR;
  426.     sgcn_irq.claim = sgcn_claim;
  427.     sgcn_irq.handler = sgcn_irq_handler;
  428.     irq_register(&sgcn_irq);
  429.    
  430.     sysinfo_set_item_val("kbd", NULL, true);
  431.     sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
  432.     sysinfo_set_item_val("kbd.devno", NULL, devno);
  433.     sysinfo_set_item_val("kbd.inr", NULL, FICTIONAL_INR);
  434.     sysinfo_set_item_val("fb.kind", NULL, 4);
  435.    
  436.     chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
  437.     stdin = &sgcn_io;
  438.     stdout = &sgcn_io;
  439. }
  440.  
  441. /** @}
  442.  */
  443.