Subversion Repositories HelenOS

Rev

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