Subversion Repositories HelenOS

Rev

Rev 3790 | Rev 3906 | 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);
  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.  * Registers the physical area of the SRAM so that the userspace SGCN
  165.  * driver can map it. Moreover, it sets some sysinfo values (SRAM address
  166.  * and SRAM size).
  167.  */
  168. static void register_sram_parea(uintptr_t sram_begin_physical)
  169. {
  170.     static parea_t sram_parea;
  171.     sram_parea.pbase = sram_begin_physical;
  172.     sram_parea.vbase = (uintptr_t) sram_begin;
  173.     sram_parea.frames = MAPPED_AREA_SIZE / FRAME_SIZE;
  174.     sram_parea.cacheable = false;
  175.     ddi_parea_register(&sram_parea);
  176.    
  177.     sysinfo_set_item_val("sram.area.size", NULL, MAPPED_AREA_SIZE);
  178.     sysinfo_set_item_val("sram.address.physical", NULL,
  179.         sram_begin_physical);
  180. }
  181.  
  182. /**
  183.  * Initializes the starting address of SRAM.
  184.  *
  185.  * The SRAM starts 0x900000 + C bytes behind the SBBC start in the
  186.  * physical memory, where C is the value read from the "iosram-toc"
  187.  * property of the "/chosen" OBP node. The sram_begin variable will
  188.  * be set to the virtual address which maps to the SRAM physical
  189.  * address.
  190.  *
  191.  * It also registers the physical area of SRAM and sets some sysinfo
  192.  * values (SRAM address and SRAM size).
  193.  */
  194. static void init_sram_begin(void)
  195. {
  196.     ofw_tree_node_t *chosen;
  197.     ofw_tree_property_t *iosram_toc;
  198.     uintptr_t sram_begin_physical;
  199.  
  200.     chosen = ofw_tree_lookup("/chosen");
  201.     if (!chosen)
  202.         panic("Cannot find '/chosen'.");
  203.  
  204.     iosram_toc = ofw_tree_getprop(chosen, "iosram-toc");
  205.     if (!iosram_toc)
  206.         panic("Cannot find property 'iosram-toc'.");
  207.     if (!iosram_toc->value)
  208.         panic("Cannot find SRAM TOC.");
  209.  
  210.     sram_begin_physical = SBBC_START + SBBC_SRAM_OFFSET
  211.         + *((uint32_t *) iosram_toc->value);
  212.     sram_begin = hw_map(sram_begin_physical, MAPPED_AREA_SIZE);
  213.    
  214.     register_sram_parea(sram_begin_physical);
  215. }
  216.  
  217. /**
  218.  * Initializes the starting address of the SGCN buffer.
  219.  *
  220.  * The offset of the SGCN buffer within SRAM is obtained from the
  221.  * SRAM table of contents. The table of contents contains
  222.  * information about several buffers, among which there is an OBP
  223.  * console buffer - this one will be used as the SGCN buffer.
  224.  *
  225.  * This function also writes the offset of the SGCN buffer within SRAM
  226.  * under the sram.buffer.offset sysinfo key.
  227.  */
  228. static void sgcn_buffer_begin_init(void)
  229. {
  230.     init_sram_begin();
  231.        
  232.     ASSERT(strcmp(SRAM_TOC->magic, SRAM_TOC_MAGIC) == 0);
  233.    
  234.     /* lookup TOC entry with the correct key */
  235.     uint32_t i;
  236.     for (i = 0; i < MAX_TOC_ENTRIES; i++) {
  237.         if (strcmp(SRAM_TOC->keys[i].key, CONSOLE_KEY) == 0)
  238.             break;
  239.     }
  240.     ASSERT(i < MAX_TOC_ENTRIES);
  241.    
  242.     sgcn_buffer_begin = sram_begin + SRAM_TOC->keys[i].offset;
  243.    
  244.     sysinfo_set_item_val("sram.buffer.offset", NULL,
  245.         SRAM_TOC->keys[i].offset);
  246. }
  247.  
  248. /**
  249.  * Default suspend/resume operation for the input device.
  250.  */
  251. static void sgcn_noop(chardev_t *d)
  252. {
  253. }
  254.  
  255. /**
  256.  * Writes a single character to the SGCN (circular) output buffer
  257.  * and updates the output write pointer so that SGCN gets to know
  258.  * that the character has been written.
  259.  */
  260. static void sgcn_do_putchar(const char c)
  261. {
  262.     uint32_t begin = SGCN_BUFFER_HEADER->out_begin;
  263.     uint32_t end = SGCN_BUFFER_HEADER->out_end;
  264.     uint32_t size = end - begin;
  265.    
  266.     /* we need pointers to volatile variables */
  267.     volatile char *buf_ptr = (volatile char *)
  268.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->out_wrptr);
  269.     volatile uint32_t *out_wrptr_ptr = &(SGCN_BUFFER_HEADER->out_wrptr);
  270.     volatile uint32_t *out_rdptr_ptr = &(SGCN_BUFFER_HEADER->out_rdptr);
  271.  
  272.     /*
  273.      * Write the character and increment the write pointer modulo the
  274.      * output buffer size. Note that if we are to rewrite a character
  275.      * which has not been read by the SGCN controller yet (i.e. the output
  276.      * buffer is full), we need to wait until the controller reads some more
  277.      * characters. We wait actively, which means that all threads waiting
  278.      * for the lock are blocked. However, this situation is
  279.      *   1) rare - the output buffer is big, so filling the whole
  280.      *             output buffer is improbable
  281.      *   2) short-lasting - it will take the controller only a fraction
  282.      *             of millisecond to pick the unread characters up
  283.      *   3) not serious - the blocked threads are those that print something
  284.      *             to user console, which is not a time-critical operation
  285.      */
  286.     uint32_t new_wrptr = (((*out_wrptr_ptr) - begin + 1) % size) + begin;
  287.     while (*out_rdptr_ptr == new_wrptr)
  288.         ;
  289.     *buf_ptr = c;
  290.     *out_wrptr_ptr = new_wrptr;
  291. }
  292.  
  293. /**
  294.  * SGCN output operation. Prints a single character to the SGCN. If the line
  295.  * feed character is written ('\n'), the carriage return character ('\r') is
  296.  * written straight away.
  297.  */
  298. static void sgcn_putchar(struct chardev * cd, const char c, bool silent)
  299. {
  300.     if (!silent) {
  301.         spinlock_lock(&sgcn_output_lock);
  302.        
  303.         sgcn_do_putchar(c);
  304.         if (c == '\n')
  305.             sgcn_do_putchar('\r');
  306.        
  307.         spinlock_unlock(&sgcn_output_lock);
  308.     }
  309. }
  310.  
  311. /**
  312.  * Called when actively reading the character. Not implemented yet.
  313.  */
  314. static char sgcn_key_read(chardev_t *d)
  315. {
  316.     return (char) 0;
  317. }
  318.  
  319. /**
  320.  * The driver works in polled mode, so no interrupt should be handled by it.
  321.  */
  322. static irq_ownership_t sgcn_claim(void)
  323. {
  324.     return IRQ_DECLINE;
  325. }
  326.  
  327. /**
  328.  * The driver works in polled mode, so no interrupt should be handled by it.
  329.  */
  330. static void sgcn_irq_handler(irq_t *irq, void *arg, ...)
  331. {
  332.     panic("Not yet implemented, SGCN works in polled mode.");
  333. }
  334.  
  335. /**
  336.  * Grabs the input for kernel.
  337.  */
  338. void sgcn_grab(void)
  339. {
  340.     ipl_t ipl = interrupts_disable();
  341.    
  342.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  343.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  344.    
  345.     /* skip all the user typed before the grab and hasn't been processed */
  346.     spinlock_lock(&sgcn_input_lock);
  347.     *in_rdptr_ptr = *in_wrptr_ptr;
  348.     spinlock_unlock(&sgcn_input_lock);
  349.  
  350.     spinlock_lock(&sgcn_irq.lock);
  351.     sgcn_irq.notif_cfg.notify = false;
  352.     spinlock_unlock(&sgcn_irq.lock);
  353.    
  354.     interrupts_restore(ipl);
  355. }
  356.  
  357. /**
  358.  * Releases the input so that userspace can use it.
  359.  */
  360. void sgcn_release(void)
  361. {
  362.     ipl_t ipl = interrupts_disable();
  363.     spinlock_lock(&sgcn_irq.lock);
  364.     if (sgcn_irq.notif_cfg.answerbox)
  365.         sgcn_irq.notif_cfg.notify = true;
  366.     spinlock_unlock(&sgcn_irq.lock);
  367.     interrupts_restore(ipl);
  368. }
  369.  
  370. /**
  371.  * Function regularly called by the keyboard polling thread. Finds out whether
  372.  * there are some unread characters in the input queue. If so, it picks them up
  373.  * and sends them to the upper layers of HelenOS.
  374.  */
  375. void sgcn_poll(void)
  376. {
  377.     uint32_t begin = SGCN_BUFFER_HEADER->in_begin;
  378.     uint32_t end = SGCN_BUFFER_HEADER->in_end;
  379.     uint32_t size = end - begin;
  380.    
  381.     spinlock_lock(&sgcn_input_lock);
  382.    
  383.     ipl_t ipl = interrupts_disable();
  384.     spinlock_lock(&sgcn_irq.lock);
  385.    
  386.     /* we need pointers to volatile variables */
  387.     volatile char *buf_ptr = (volatile char *)
  388.         SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  389.     volatile uint32_t *in_wrptr_ptr = &(SGCN_BUFFER_HEADER->in_wrptr);
  390.     volatile uint32_t *in_rdptr_ptr = &(SGCN_BUFFER_HEADER->in_rdptr);
  391.    
  392.     if (*in_rdptr_ptr != *in_wrptr_ptr) {
  393.         if (sgcn_irq.notif_cfg.notify && sgcn_irq.notif_cfg.answerbox) {
  394.             ipc_irq_send_notif(&sgcn_irq);
  395.             spinlock_unlock(&sgcn_irq.lock);
  396.             interrupts_restore(ipl);
  397.             spinlock_unlock(&sgcn_input_lock);
  398.             return;
  399.         }
  400.     }
  401.    
  402.     spinlock_unlock(&sgcn_irq.lock);
  403.     interrupts_restore(ipl);   
  404.  
  405.     while (*in_rdptr_ptr != *in_wrptr_ptr) {
  406.        
  407.         buf_ptr = (volatile char *)
  408.             SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
  409.         char c = *buf_ptr;
  410.         *in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
  411.            
  412.         if (c == '\r') {
  413.             c = '\n';
  414.         }
  415.         chardev_push_character(&sgcn_io, c);   
  416.     }  
  417.    
  418.     spinlock_unlock(&sgcn_input_lock);
  419. }
  420.  
  421. /**
  422.  * A public function which initializes I/O from/to Serengeti console
  423.  * and sets it as a default input/output.
  424.  */
  425. void sgcn_init(void)
  426. {
  427.     sgcn_buffer_begin_init();
  428.  
  429.     kbd_type = KBD_SGCN;
  430.  
  431.     devno_t devno = device_assign_devno();
  432.     irq_initialize(&sgcn_irq);
  433.     sgcn_irq.devno = devno;
  434.     sgcn_irq.inr = FICTIONAL_INR;
  435.     sgcn_irq.claim = sgcn_claim;
  436.     sgcn_irq.handler = sgcn_irq_handler;
  437.     irq_register(&sgcn_irq);
  438.    
  439.     sysinfo_set_item_val("kbd", NULL, true);
  440.     sysinfo_set_item_val("kbd.type", NULL, KBD_SGCN);
  441.     sysinfo_set_item_val("kbd.devno", NULL, devno);
  442.     sysinfo_set_item_val("kbd.inr", NULL, FICTIONAL_INR);
  443.     sysinfo_set_item_val("fb.kind", NULL, 4);
  444.    
  445.     chardev_initialize("sgcn_io", &sgcn_io, &sgcn_ops);
  446.     stdin = &sgcn_io;
  447.     stdout = &sgcn_io;
  448. }
  449.  
  450. /** @}
  451.  */
  452.