Rev 4676 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 4651 | pillai | 1 | /* |
| 2 | * Copyright (c) 2009 Vineeth Pillai |
||
| 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 arm32integratorcp |
||
| 30 | * @{ |
||
| 31 | */ |
||
| 32 | /** @file |
||
| 33 | * @brief ICP drivers. |
||
| 34 | */ |
||
| 35 | |||
| 36 | #include <interrupt.h> |
||
| 37 | #include <ipc/irq.h> |
||
| 38 | #include <console/chardev.h> |
||
| 39 | #include <genarch/drivers/pl050/pl050.h> |
||
| 40 | #include <genarch/kbrd/kbrd.h> |
||
| 41 | #include <console/console.h> |
||
| 42 | #include <sysinfo/sysinfo.h> |
||
| 43 | #include <print.h> |
||
| 44 | #include <ddi/device.h> |
||
| 45 | #include <mm/page.h> |
||
| 46 | #include <mm/frame.h> |
||
| 47 | #include <arch/mm/frame.h> |
||
| 48 | #include <arch/mach/integratorcp/integratorcp.h> |
||
| 49 | #include <genarch/fb/fb.h> |
||
| 50 | #include <genarch/fb/visuals.h> |
||
| 51 | #include <ddi/ddi.h> |
||
| 52 | #include <print.h> |
||
| 53 | |||
| 54 | #define SDRAM_SIZE (sdram[((*(uint32_t *)(ICP_CMCR+ICP_SDRAMCR_OFFSET) & ICP_SDRAM_MASK) >> 2)]) |
||
| 55 | static parea_t fb_parea; |
||
| 56 | static icp_hw_map_t icp_hw_map; |
||
| 57 | static irq_t icp_timer_irq; |
||
| 58 | struct arm_machine_ops machine_ops = { |
||
| 59 | MACHINE_GENFUNC, |
||
| 60 | MACHINE_GENFUNC, |
||
| 61 | icp_init, |
||
| 62 | icp_timer_irq_start, |
||
| 63 | icp_cpu_halt, |
||
| 64 | icp_get_memory_size, |
||
| 65 | icp_fb_init, |
||
| 66 | icp_irq_exception, |
||
| 67 | icp_get_fb_address, |
||
| 68 | icp_frame_init, |
||
| 69 | icp_output_init, |
||
| 70 | icp_input_init |
||
| 71 | }; |
||
| 72 | |||
| 73 | static bool hw_map_init_called = false; |
||
| 74 | static bool vga_init = false; |
||
| 75 | uint32_t sdram[8] = { |
||
| 76 | 16777216, /* 16mb */ |
||
| 77 | 33554432, /* 32mb */ |
||
| 78 | 67108864, /* 64mb */ |
||
| 79 | 134217728, /* 128mb */ |
||
| 80 | 268435456, /* 256mb */ |
||
| 81 | 0, /* Reserverd */ |
||
| 82 | 0, /* Reserverd */ |
||
| 83 | |||
| 84 | }; |
||
| 85 | |||
| 86 | void icp_vga_init(void); |
||
| 87 | |||
| 88 | /** Initializes the vga |
||
| 89 | * |
||
| 90 | */ |
||
| 91 | void icp_vga_init(void) |
||
| 92 | { |
||
| 93 | *(uint32_t*)((char *)(icp_hw_map.cmcr)+0x14) = 0xA05F0000; |
||
| 94 | *(uint32_t*)((char *)(icp_hw_map.cmcr)+0x1C) = 0x12C11000; |
||
| 95 | *(uint32_t*)icp_hw_map.vga = 0x3F1F3F9C; |
||
| 96 | *(uint32_t*)((char *)(icp_hw_map.vga) + 0x4) = 0x080B61DF; |
||
| 97 | *(uint32_t*)((char *)(icp_hw_map.vga) + 0x8) = 0x067F3800; |
||
| 98 | *(uint32_t*)((char *)(icp_hw_map.vga) + 0x10) = ICP_FB; |
||
| 99 | *(uint32_t *)((char *)(icp_hw_map.vga) + 0x1C) = 0x182B; |
||
| 100 | *(uint32_t*)((char *)(icp_hw_map.cmcr)+0xC) = 0x33805000; |
||
| 101 | |||
| 102 | } |
||
| 103 | |||
| 104 | /** Returns the mask of active interrupts. */ |
||
| 105 | static inline uint32_t icp_irqc_get_sources(void) |
||
| 106 | { |
||
| 107 | return *((uint32_t *) icp_hw_map.irqc); |
||
| 108 | } |
||
| 109 | |||
| 110 | |||
| 111 | /** Masks interrupt. |
||
| 112 | * |
||
| 113 | * @param irq interrupt number |
||
| 114 | */ |
||
| 115 | static inline void icp_irqc_mask(uint32_t irq) |
||
| 116 | { |
||
| 117 | *((uint32_t *) icp_hw_map.irqc_mask) = (1 << irq); |
||
| 118 | } |
||
| 119 | |||
| 120 | |||
| 121 | /** Unmasks interrupt. |
||
| 122 | * |
||
| 123 | * @param irq interrupt number |
||
| 124 | */ |
||
| 125 | static inline void icp_irqc_unmask(uint32_t irq) |
||
| 126 | { |
||
| 127 | *((uint32_t *) icp_hw_map.irqc_unmask) |= (1 << irq); |
||
| 128 | } |
||
| 129 | |||
| 130 | /** Initializes the icp frame buffer */ |
||
| 131 | void icp_fb_init(void) |
||
| 132 | { |
||
| 133 | fb_properties_t prop = { |
||
| 134 | .addr = 0, |
||
| 135 | .offset = 0, |
||
| 136 | .x = 640, |
||
| 137 | .y = 480, |
||
| 138 | .scan = 2560, |
||
| 4676 | jermar | 139 | .visual = VISUAL_BGR_0_8_8_8, |
| 4651 | pillai | 140 | }; |
| 141 | prop.addr = icp_get_fb_address(); |
||
| 142 | fb_init(&prop); |
||
| 143 | fb_parea.pbase = ICP_FB; |
||
| 144 | fb_parea.frames = 300; |
||
| 145 | ddi_parea_register(&fb_parea); |
||
| 146 | } |
||
| 147 | |||
| 148 | /** Initializes icp_hw_map. */ |
||
| 149 | void icp_init(void) |
||
| 150 | { |
||
| 151 | icp_hw_map.uart = hw_map(ICP_UART, PAGE_SIZE); |
||
| 152 | icp_hw_map.kbd_ctrl = hw_map(ICP_KBD, PAGE_SIZE); |
||
| 153 | icp_hw_map.kbd_stat = icp_hw_map.kbd_ctrl + ICP_KBD_STAT; |
||
| 154 | icp_hw_map.kbd_data = icp_hw_map.kbd_ctrl + ICP_KBD_DATA; |
||
| 155 | icp_hw_map.kbd_intstat = icp_hw_map.kbd_ctrl + ICP_KBD_INTR_STAT; |
||
| 156 | icp_hw_map.rtc = hw_map(ICP_RTC, PAGE_SIZE); |
||
| 157 | icp_hw_map.rtc1_load = icp_hw_map.rtc + ICP_RTC1_LOAD_OFFSET; |
||
| 158 | icp_hw_map.rtc1_read = icp_hw_map.rtc + ICP_RTC1_READ_OFFSET; |
||
| 159 | icp_hw_map.rtc1_ctl = icp_hw_map.rtc + ICP_RTC1_CTL_OFFSET; |
||
| 160 | icp_hw_map.rtc1_intrclr = icp_hw_map.rtc + ICP_RTC1_INTRCLR_OFFSET; |
||
| 161 | icp_hw_map.rtc1_bgload = icp_hw_map.rtc + ICP_RTC1_BGLOAD_OFFSET; |
||
| 162 | icp_hw_map.rtc1_intrstat = icp_hw_map.rtc + ICP_RTC1_INTRSTAT_OFFSET; |
||
| 163 | |||
| 164 | icp_hw_map.irqc = hw_map(ICP_IRQC, PAGE_SIZE); |
||
| 165 | icp_hw_map.irqc_mask = icp_hw_map.irqc + ICP_IRQC_MASK_OFFSET; |
||
| 166 | icp_hw_map.irqc_unmask = icp_hw_map.irqc + ICP_IRQC_UNMASK_OFFSET; |
||
| 167 | icp_hw_map.cmcr = hw_map(ICP_CMCR, PAGE_SIZE); |
||
| 168 | icp_hw_map.sdramcr = icp_hw_map.cmcr + ICP_SDRAMCR_OFFSET; |
||
| 169 | icp_hw_map.vga = hw_map(ICP_VGA, PAGE_SIZE); |
||
| 170 | |||
| 171 | hw_map_init_called = true; |
||
| 172 | } |
||
| 173 | |||
| 174 | |||
| 175 | /** Acquire console back for kernel. */ |
||
| 176 | void icp_grab_console(void) |
||
| 177 | { |
||
| 178 | } |
||
| 179 | |||
| 180 | /** Return console to userspace. */ |
||
| 181 | void icp_release_console(void) |
||
| 182 | { |
||
| 183 | } |
||
| 184 | |||
| 185 | /** Starts icp Real Time Clock device, which asserts regular interrupts. |
||
| 186 | * |
||
| 187 | * @param frequency Interrupts frequency (0 disables RTC). |
||
| 188 | */ |
||
| 189 | static void icp_timer_start(uint32_t frequency) |
||
| 190 | { |
||
| 191 | icp_irqc_mask(ICP_TIMER_IRQ); |
||
| 192 | *((uint32_t*) icp_hw_map.rtc1_load) = frequency; |
||
| 193 | *((uint32_t*) icp_hw_map.rtc1_bgload) = frequency; |
||
| 194 | *((uint32_t*) icp_hw_map.rtc1_ctl) = ICP_RTC_CTL_VALUE; |
||
| 195 | icp_irqc_unmask(ICP_TIMER_IRQ); |
||
| 196 | } |
||
| 197 | |||
| 198 | static irq_ownership_t icp_timer_claim(irq_t *irq) |
||
| 199 | { |
||
| 200 | if (icp_hw_map.rtc1_intrstat) { |
||
| 201 | *((uint32_t*) icp_hw_map.rtc1_intrclr) = 1; |
||
| 202 | return IRQ_ACCEPT; |
||
| 203 | } else |
||
| 204 | return IRQ_DECLINE; |
||
| 205 | } |
||
| 206 | |||
| 207 | /** Timer interrupt handler. |
||
| 208 | * |
||
| 209 | * @param irq Interrupt information. |
||
| 210 | * @param arg Not used. |
||
| 211 | */ |
||
| 212 | static void icp_timer_irq_handler(irq_t *irq) |
||
| 213 | { |
||
| 214 | /* |
||
| 215 | * We are holding a lock which prevents preemption. |
||
| 216 | * Release the lock, call clock() and reacquire the lock again. |
||
| 217 | */ |
||
| 218 | |||
| 219 | spinlock_unlock(&irq->lock); |
||
| 220 | clock(); |
||
| 221 | spinlock_lock(&irq->lock); |
||
| 222 | |||
| 223 | } |
||
| 224 | |||
| 225 | /** Initializes and registers timer interrupt handler. */ |
||
| 226 | static void icp_timer_irq_init(void) |
||
| 227 | { |
||
| 228 | irq_initialize(&icp_timer_irq); |
||
| 229 | icp_timer_irq.devno = device_assign_devno(); |
||
| 230 | icp_timer_irq.inr = ICP_TIMER_IRQ; |
||
| 231 | icp_timer_irq.claim = icp_timer_claim; |
||
| 232 | icp_timer_irq.handler = icp_timer_irq_handler; |
||
| 233 | |||
| 234 | irq_register(&icp_timer_irq); |
||
| 235 | } |
||
| 236 | |||
| 237 | |||
| 238 | /** Starts timer. |
||
| 239 | * |
||
| 240 | * Initiates regular timer interrupts after initializing |
||
| 241 | * corresponding interrupt handler. |
||
| 242 | */ |
||
| 243 | void icp_timer_irq_start(void) |
||
| 244 | { |
||
| 245 | icp_timer_irq_init(); |
||
| 246 | icp_timer_start(ICP_TIMER_FREQ); |
||
| 247 | } |
||
| 248 | |||
| 249 | /** Returns the size of emulated memory. |
||
| 250 | * |
||
| 251 | * @return Size in bytes. |
||
| 252 | */ |
||
| 253 | size_t icp_get_memory_size(void) |
||
| 254 | { |
||
| 255 | if (hw_map_init_called) { |
||
| 256 | return (sdram[((*(uint32_t *)icp_hw_map.sdramcr & ICP_SDRAM_MASK) >> 2)]); |
||
| 257 | } else { |
||
| 258 | return SDRAM_SIZE; |
||
| 259 | } |
||
| 260 | |||
| 261 | } |
||
| 262 | |||
| 263 | /** Stops icp. */ |
||
| 264 | void icp_cpu_halt(void) |
||
| 265 | { |
||
| 266 | while (1); |
||
| 267 | } |
||
| 268 | |||
| 269 | /** interrupt exception handler. |
||
| 270 | * |
||
| 271 | * Determines sources of the interrupt from interrupt controller and |
||
| 272 | * calls high-level handlers for them. |
||
| 273 | * |
||
| 274 | * @param exc_no Interrupt exception number. |
||
| 275 | * @param istate Saved processor state. |
||
| 276 | */ |
||
| 277 | void icp_irq_exception(int exc_no, istate_t *istate) |
||
| 278 | { |
||
| 279 | uint32_t sources = icp_irqc_get_sources(); |
||
| 280 | int i; |
||
| 281 | |||
| 282 | for (i = 0; i < ICP_IRQC_MAX_IRQ; i++) { |
||
| 283 | if (sources & (1 << i)) { |
||
| 284 | irq_t *irq = irq_dispatch_and_lock(i); |
||
| 285 | if (irq) { |
||
| 286 | /* The IRQ handler was found. */ |
||
| 287 | irq->handler(irq); |
||
| 288 | spinlock_unlock(&irq->lock); |
||
| 289 | } else { |
||
| 290 | /* Spurious interrupt.*/ |
||
| 291 | printf("cpu%d: spurious interrupt (inum=%d)\n", |
||
| 292 | CPU->id, i); |
||
| 293 | } |
||
| 294 | } |
||
| 295 | } |
||
| 296 | } |
||
| 297 | |||
| 298 | /** Returns address of framebuffer device. |
||
| 299 | * |
||
| 300 | * @return Address of framebuffer device. |
||
| 301 | */ |
||
| 302 | uintptr_t icp_get_fb_address(void) |
||
| 303 | { |
||
| 304 | if (!vga_init) { |
||
| 305 | icp_vga_init(); |
||
| 306 | vga_init = true; |
||
| 307 | } |
||
| 308 | return (uintptr_t) ICP_FB; |
||
| 309 | } |
||
| 310 | |||
| 311 | /* |
||
| 312 | * Integrator specific frame initialization |
||
| 313 | */ |
||
| 314 | void |
||
| 315 | icp_frame_init(void) |
||
| 316 | { |
||
| 317 | frame_mark_unavailable(ICP_FB_FRAME, ICP_FB_NUM_FRAME); |
||
| 318 | frame_mark_unavailable(0, 256); |
||
| 319 | } |
||
| 320 | |||
| 321 | void icp_output_init(void) |
||
| 322 | { |
||
| 323 | } |
||
| 324 | |||
| 325 | void icp_input_init(void) |
||
| 326 | { |
||
| 327 | |||
| 328 | pl050_t *pl050 = malloc(sizeof(pl050_t), FRAME_ATOMIC); |
||
| 329 | pl050->status = (ioport8_t *)icp_hw_map.kbd_stat; |
||
| 330 | pl050->data = (ioport8_t *)icp_hw_map.kbd_data; |
||
| 331 | pl050->ctrl = (ioport8_t *)icp_hw_map.kbd_ctrl; |
||
| 332 | |||
| 333 | pl050_instance_t *pl050_instance = pl050_init(pl050, ICP_KBD_IRQ); |
||
| 334 | if (pl050_instance) { |
||
| 335 | kbrd_instance_t *kbrd_instance = kbrd_init(); |
||
| 336 | if (kbrd_instance) { |
||
| 337 | icp_irqc_mask(ICP_KBD_IRQ); |
||
| 338 | indev_t *sink = stdin_wire(); |
||
| 339 | indev_t *kbrd = kbrd_wire(kbrd_instance, sink); |
||
| 340 | pl050_wire(pl050_instance, kbrd); |
||
| 341 | icp_irqc_unmask(ICP_KBD_IRQ); |
||
| 342 | } |
||
| 343 | } |
||
| 344 | |||
| 345 | /* |
||
| 346 | * This is the necessary evil until the userspace driver is entirely |
||
| 347 | * self-sufficient. |
||
| 348 | */ |
||
| 349 | sysinfo_set_item_val("kbd", NULL, true); |
||
| 350 | sysinfo_set_item_val("kbd.inr", NULL, ICP_KBD_IRQ); |
||
| 351 | sysinfo_set_item_val("kbd.address.status", NULL, |
||
| 352 | (uintptr_t) icp_hw_map.kbd_stat); |
||
| 353 | sysinfo_set_item_val("kbd.address.data", NULL, |
||
| 354 | (uintptr_t) icp_hw_map.kbd_data); |
||
| 355 | |||
| 356 | } |
||
| 357 | |||
| 358 | |||
| 359 | /** @} |
||
| 360 | */ |