Subversion Repositories HelenOS

Rev

Rev 4343 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2007 Michal Kebrt, Petr Stepan
  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 arm32gxemul
  30.  * @{
  31.  */
  32. /** @file
  33.  *  @brief GXemul drivers.
  34.  */
  35.  
  36. #include <interrupt.h>
  37. #include <console/chardev.h>
  38. #include <arch/drivers/gxemul.h>
  39. #include <console/console.h>
  40. #include <sysinfo/sysinfo.h>
  41. #include <print.h>
  42. #include <ddi/device.h>
  43. #include <mm/page.h>
  44. #include <arch/machine.h>
  45. #include <arch/debug/print.h>
  46.  
  47. /* Addresses of devices. */
  48. #define GXEMUL_VIDEORAM            0x10000000
  49. #define GXEMUL_KBD                 0x10000000
  50. #define GXEMUL_HALT_OFFSET         0x10
  51. #define GXEMUL_RTC                 0x15000000
  52. #define GXEMUL_RTC_FREQ_OFFSET     0x100
  53. #define GXEMUL_RTC_ACK_OFFSET      0x110
  54. #define GXEMUL_IRQC                0x16000000
  55. #define GXEMUL_IRQC_MASK_OFFSET    0x4
  56. #define GXEMUL_IRQC_UNMASK_OFFSET  0x8
  57. #define GXEMUL_MP                  0x11000000
  58. #define GXEMUL_MP_MEMSIZE_OFFSET   0x0090
  59. #define GXEMUL_FB                  0x12000000
  60.  
  61. /* IRQs */
  62. #define GXEMUL_KBD_IRQ      2
  63. #define GXEMUL_TIMER_IRQ    4
  64.  
  65. static gxemul_hw_map_t gxemul_hw_map;
  66. static chardev_t console;
  67. static irq_t gxemul_console_irq;
  68. static irq_t gxemul_timer_irq;
  69.  
  70. static bool hw_map_init_called = false;
  71.  
  72. static void gxemul_kbd_enable(chardev_t *dev);
  73. static void gxemul_kbd_disable(chardev_t *dev);
  74. static void gxemul_write(chardev_t *dev, const char ch, bool silent);
  75. static char gxemul_do_read(chardev_t *dev);
  76.  
  77. static chardev_operations_t gxemul_ops = {
  78.     .resume = gxemul_kbd_enable,
  79.     .suspend = gxemul_kbd_disable,
  80.     .write = gxemul_write,
  81.     .read = gxemul_do_read,
  82. };
  83.  
  84.  
  85. /** Returns the mask of active interrupts. */
  86. static inline uint32_t gxemul_irqc_get_sources(void)
  87. {
  88.     return *((uint32_t *) gxemul_hw_map.irqc);
  89. }
  90.  
  91.  
  92. /** Masks interrupt.
  93.  *
  94.  * @param irq interrupt number
  95.  */
  96. static inline void gxemul_irqc_mask(uint32_t irq)
  97. {
  98.     *((uint32_t *) gxemul_hw_map.irqc_mask) = irq;
  99. }
  100.  
  101.  
  102. /** Unmasks interrupt.
  103.  *
  104.  * @param irq interrupt number
  105.  */
  106. static inline void gxemul_irqc_unmask(uint32_t irq)
  107. {
  108.     *((uint32_t *) gxemul_hw_map.irqc_unmask) = irq;
  109. }
  110.  
  111.  
  112. /** Initializes #gxemul_hw_map. */
  113. void gxemul_hw_map_init(void)
  114. {
  115.     gxemul_hw_map.videoram = hw_map(GXEMUL_VIDEORAM, PAGE_SIZE);
  116.     gxemul_hw_map.kbd = hw_map(GXEMUL_KBD, PAGE_SIZE);
  117.     gxemul_hw_map.rtc = hw_map(GXEMUL_RTC, PAGE_SIZE);
  118.     gxemul_hw_map.irqc = hw_map(GXEMUL_IRQC, PAGE_SIZE);
  119.  
  120.     gxemul_hw_map.rtc_freq = gxemul_hw_map.rtc + GXEMUL_RTC_FREQ_OFFSET;
  121.     gxemul_hw_map.rtc_ack = gxemul_hw_map.rtc + GXEMUL_RTC_ACK_OFFSET;
  122.     gxemul_hw_map.irqc_mask = gxemul_hw_map.irqc + GXEMUL_IRQC_MASK_OFFSET;
  123.     gxemul_hw_map.irqc_unmask = gxemul_hw_map.irqc +
  124.         GXEMUL_IRQC_UNMASK_OFFSET;
  125.  
  126.     hw_map_init_called = true;
  127. }
  128.  
  129.  
  130. /** Putchar that works with gxemul.
  131.  *
  132.  * @param dev Not used.
  133.  * @param ch Characted to be printed.
  134.  */
  135. static void gxemul_write(chardev_t *dev, const char ch, bool silent)
  136. {
  137.     if (!silent)
  138.         *((char *) gxemul_hw_map.videoram) = ch;
  139. }
  140.  
  141. /** Enables gxemul keyboard (interrupt unmasked).
  142.  *
  143.  * @param dev Not used.
  144.  *
  145.  * Called from getc().
  146.  */
  147. static void gxemul_kbd_enable(chardev_t *dev)
  148. {
  149.     gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
  150. }
  151.  
  152. /** Disables gxemul keyboard (interrupt masked).
  153.  *
  154.  * @param dev not used
  155.  *
  156.  * Called from getc().
  157.  */
  158. static void gxemul_kbd_disable(chardev_t *dev)
  159. {
  160.     gxemul_irqc_mask(GXEMUL_KBD_IRQ);
  161. }
  162.  
  163. /** Read character using polling, assume interrupts disabled.
  164.  *
  165.  *  @param dev Not used.
  166.  */
  167. static char gxemul_do_read(chardev_t *dev)
  168. {
  169.     char ch;
  170.  
  171.     while (1) {
  172.         ch = *((volatile char *) gxemul_hw_map.kbd);
  173.         if (ch) {
  174.             if (ch == '\r')
  175.                 return '\n';
  176.             if (ch == 0x7f)
  177.                 return '\b';
  178.             return ch;
  179.         }
  180.     }
  181. }
  182.  
  183. /** Process keyboard interrupt.
  184.  *  
  185.  *  @param irq IRQ information.
  186.  */
  187. static void gxemul_irq_handler(irq_t *irq)
  188. {
  189.     char ch = 0;
  190.        
  191.     ch = *((char *) gxemul_hw_map.kbd);
  192.     if (ch == '\r') {
  193.         ch = '\n';
  194.     }
  195.     if (ch == 0x7f) {
  196.         ch = '\b';
  197.     }
  198.     chardev_push_character(&console, ch);
  199. }
  200.  
  201. static irq_ownership_t gxemul_claim(irq_t *irq)
  202. {
  203.     return IRQ_ACCEPT;
  204. }
  205.  
  206. /** Initializes console object representing gxemul console.
  207.  *
  208.  *  @param devno device number.
  209.  */
  210. void gxemul_console_init(devno_t devno)
  211. {
  212.     chardev_initialize("gxemul_console", &console, &gxemul_ops);
  213.     stdin = &console;
  214.     stdout = &console;
  215.    
  216.     irq_initialize(&gxemul_console_irq);
  217.     gxemul_console_irq.devno = devno;
  218.     gxemul_console_irq.inr = GXEMUL_KBD_IRQ;
  219.     gxemul_console_irq.claim = gxemul_claim;
  220.     gxemul_console_irq.handler = gxemul_irq_handler;
  221.     irq_register(&gxemul_console_irq);
  222.    
  223.     gxemul_irqc_unmask(GXEMUL_KBD_IRQ);
  224.    
  225.     sysinfo_set_item_val("kbd", NULL, true);
  226.     sysinfo_set_item_val("kbd.devno", NULL, devno);
  227.     sysinfo_set_item_val("kbd.inr", NULL, GXEMUL_KBD_IRQ);
  228.     sysinfo_set_item_val("kbd.address.virtual", NULL, gxemul_hw_map.kbd);
  229. }
  230.  
  231. /** Starts gxemul Real Time Clock device, which asserts regular interrupts.
  232.  *
  233.  * @param frequency Interrupts frequency (0 disables RTC).
  234.  */
  235. static void gxemul_timer_start(uint32_t frequency)
  236. {
  237.     *((uint32_t*) gxemul_hw_map.rtc_freq) = frequency;
  238. }
  239.  
  240. static irq_ownership_t gxemul_timer_claim(irq_t *irq)
  241. {
  242.     return IRQ_ACCEPT;
  243. }
  244.  
  245. /** Timer interrupt handler.
  246.  *
  247.  * @param irq Interrupt information.
  248.  * @param arg Not used.
  249.  */
  250. static void gxemul_timer_irq_handler(irq_t *irq)
  251. {
  252.     /*
  253.     * We are holding a lock which prevents preemption.
  254.     * Release the lock, call clock() and reacquire the lock again.
  255.     */
  256.     spinlock_unlock(&irq->lock);
  257.     clock();
  258.     spinlock_lock(&irq->lock);
  259.  
  260.     /* acknowledge tick */
  261.     *((uint32_t*) gxemul_hw_map.rtc_ack) = 0;
  262. }
  263.  
  264. /** Initializes and registers timer interrupt handler. */
  265. static void gxemul_timer_irq_init(void)
  266. {
  267.     irq_initialize(&gxemul_timer_irq);
  268.     gxemul_timer_irq.devno = device_assign_devno();
  269.     gxemul_timer_irq.inr = GXEMUL_TIMER_IRQ;
  270.     gxemul_timer_irq.claim = gxemul_timer_claim;
  271.     gxemul_timer_irq.handler = gxemul_timer_irq_handler;
  272.  
  273.     irq_register(&gxemul_timer_irq);
  274. }
  275.  
  276.  
  277. /** Starts timer.
  278.  *
  279.  * Initiates regular timer interrupts after initializing
  280.  * corresponding interrupt handler.
  281.  */
  282. void gxemul_timer_irq_start(void)
  283. {
  284.     gxemul_timer_irq_init();
  285.     gxemul_timer_start(GXEMUL_TIMER_FREQ);
  286. }
  287.  
  288. /** Returns the size of emulated memory.
  289.  *
  290.  * @return Size in bytes.
  291.  */
  292. size_t gxemul_get_memory_size(void)
  293. {
  294.     return  *((int *) (GXEMUL_MP + GXEMUL_MP_MEMSIZE_OFFSET));
  295. }
  296.  
  297. /** Prints a character.
  298.  *
  299.  *  @param ch Character to be printed.
  300.  */
  301. void gxemul_debug_putc(char ch)
  302. {
  303.     char *addr = 0;
  304.     if (!hw_map_init_called) {
  305.         addr = (char *) GXEMUL_KBD;
  306.     } else {
  307.         addr = (char *) gxemul_hw_map.videoram;
  308.     }
  309.  
  310.     *(addr) = ch;
  311. }
  312.  
  313. /** Stops gxemul. */
  314. void gxemul_cpu_halt(void)
  315. {
  316.     char * addr = 0;
  317.     if (!hw_map_init_called) {
  318.         addr = (char *) GXEMUL_KBD;
  319.     } else {
  320.         addr = (char *) gxemul_hw_map.videoram;
  321.     }
  322.    
  323.     *(addr + GXEMUL_HALT_OFFSET) = '\0';
  324. }
  325.  
  326. /** Gxemul specific interrupt exception handler.
  327.  *
  328.  * Determines sources of the interrupt from interrupt controller and
  329.  * calls high-level handlers for them.
  330.  *
  331.  * @param exc_no Interrupt exception number.
  332.  * @param istate Saved processor state.
  333.  */
  334. void gxemul_irq_exception(int exc_no, istate_t *istate)
  335. {
  336.     uint32_t sources = gxemul_irqc_get_sources();
  337.     int i;
  338.    
  339.     for (i = 0; i < GXEMUL_IRQC_MAX_IRQ; i++) {
  340.         if (sources & (1 << i)) {
  341.             irq_t *irq = irq_dispatch_and_lock(i);
  342.             if (irq) {
  343.                 /* The IRQ handler was found. */
  344.                 irq->handler(irq);
  345.                 spinlock_unlock(&irq->lock);
  346.             } else {
  347.                 /* Spurious interrupt.*/
  348.                 dprintf("cpu%d: spurious interrupt (inum=%d)\n",
  349.                     CPU->id, i);
  350.             }
  351.         }
  352.     }
  353. }
  354.  
  355. /** Returns address of framebuffer device.
  356.  *
  357.  *  @return Address of framebuffer device.
  358.  */
  359. uintptr_t gxemul_get_fb_address(void)
  360. {
  361.     return (uintptr_t) GXEMUL_FB;
  362. }
  363.  
  364. /** @}
  365.  */
  366.