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