0,0 → 1,360 |
/* |
* Copyright (c) 2009 Vineeth Pillai |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
|
/** @addtogroup arm32integratorcp |
* @{ |
*/ |
/** @file |
* @brief ICP drivers. |
*/ |
|
#include <interrupt.h> |
#include <ipc/irq.h> |
#include <console/chardev.h> |
#include <genarch/drivers/pl050/pl050.h> |
#include <genarch/kbrd/kbrd.h> |
#include <console/console.h> |
#include <sysinfo/sysinfo.h> |
#include <print.h> |
#include <ddi/device.h> |
#include <mm/page.h> |
#include <mm/frame.h> |
#include <arch/mm/frame.h> |
#include <arch/mach/integratorcp/integratorcp.h> |
#include <genarch/fb/fb.h> |
#include <genarch/fb/visuals.h> |
#include <ddi/ddi.h> |
#include <print.h> |
|
#define SDRAM_SIZE (sdram[((*(uint32_t *)(ICP_CMCR+ICP_SDRAMCR_OFFSET) & ICP_SDRAM_MASK) >> 2)]) |
static parea_t fb_parea; |
static icp_hw_map_t icp_hw_map; |
static irq_t icp_timer_irq; |
struct arm_machine_ops machine_ops = { |
MACHINE_GENFUNC, |
MACHINE_GENFUNC, |
icp_init, |
icp_timer_irq_start, |
icp_cpu_halt, |
icp_get_memory_size, |
icp_fb_init, |
icp_irq_exception, |
icp_get_fb_address, |
icp_frame_init, |
icp_output_init, |
icp_input_init |
}; |
|
static bool hw_map_init_called = false; |
static bool vga_init = false; |
uint32_t sdram[8] = { |
16777216, /* 16mb */ |
33554432, /* 32mb */ |
67108864, /* 64mb */ |
134217728, /* 128mb */ |
268435456, /* 256mb */ |
0, /* Reserverd */ |
0, /* Reserverd */ |
0 /* Reserverd */ |
}; |
|
void icp_vga_init(void); |
|
/** Initializes the vga |
* |
*/ |
void icp_vga_init(void) |
{ |
*(uint32_t*)((char *)(icp_hw_map.cmcr)+0x14) = 0xA05F0000; |
*(uint32_t*)((char *)(icp_hw_map.cmcr)+0x1C) = 0x12C11000; |
*(uint32_t*)icp_hw_map.vga = 0x3F1F3F9C; |
*(uint32_t*)((char *)(icp_hw_map.vga) + 0x4) = 0x080B61DF; |
*(uint32_t*)((char *)(icp_hw_map.vga) + 0x8) = 0x067F3800; |
*(uint32_t*)((char *)(icp_hw_map.vga) + 0x10) = ICP_FB; |
*(uint32_t *)((char *)(icp_hw_map.vga) + 0x1C) = 0x182B; |
*(uint32_t*)((char *)(icp_hw_map.cmcr)+0xC) = 0x33805000; |
|
} |
|
/** Returns the mask of active interrupts. */ |
static inline uint32_t icp_irqc_get_sources(void) |
{ |
return *((uint32_t *) icp_hw_map.irqc); |
} |
|
|
/** Masks interrupt. |
* |
* @param irq interrupt number |
*/ |
static inline void icp_irqc_mask(uint32_t irq) |
{ |
*((uint32_t *) icp_hw_map.irqc_mask) = (1 << irq); |
} |
|
|
/** Unmasks interrupt. |
* |
* @param irq interrupt number |
*/ |
static inline void icp_irqc_unmask(uint32_t irq) |
{ |
*((uint32_t *) icp_hw_map.irqc_unmask) |= (1 << irq); |
} |
|
/** Initializes the icp frame buffer */ |
void icp_fb_init(void) |
{ |
fb_properties_t prop = { |
.addr = 0, |
.offset = 0, |
.x = 640, |
.y = 480, |
.scan = 2560, |
.visual = VISUAL_RGB_8_8_8_0, |
}; |
prop.addr = icp_get_fb_address(); |
fb_init(&prop); |
fb_parea.pbase = ICP_FB; |
fb_parea.frames = 300; |
ddi_parea_register(&fb_parea); |
} |
|
/** Initializes icp_hw_map. */ |
void icp_init(void) |
{ |
icp_hw_map.uart = hw_map(ICP_UART, PAGE_SIZE); |
icp_hw_map.kbd_ctrl = hw_map(ICP_KBD, PAGE_SIZE); |
icp_hw_map.kbd_stat = icp_hw_map.kbd_ctrl + ICP_KBD_STAT; |
icp_hw_map.kbd_data = icp_hw_map.kbd_ctrl + ICP_KBD_DATA; |
icp_hw_map.kbd_intstat = icp_hw_map.kbd_ctrl + ICP_KBD_INTR_STAT; |
icp_hw_map.rtc = hw_map(ICP_RTC, PAGE_SIZE); |
icp_hw_map.rtc1_load = icp_hw_map.rtc + ICP_RTC1_LOAD_OFFSET; |
icp_hw_map.rtc1_read = icp_hw_map.rtc + ICP_RTC1_READ_OFFSET; |
icp_hw_map.rtc1_ctl = icp_hw_map.rtc + ICP_RTC1_CTL_OFFSET; |
icp_hw_map.rtc1_intrclr = icp_hw_map.rtc + ICP_RTC1_INTRCLR_OFFSET; |
icp_hw_map.rtc1_bgload = icp_hw_map.rtc + ICP_RTC1_BGLOAD_OFFSET; |
icp_hw_map.rtc1_intrstat = icp_hw_map.rtc + ICP_RTC1_INTRSTAT_OFFSET; |
|
icp_hw_map.irqc = hw_map(ICP_IRQC, PAGE_SIZE); |
icp_hw_map.irqc_mask = icp_hw_map.irqc + ICP_IRQC_MASK_OFFSET; |
icp_hw_map.irqc_unmask = icp_hw_map.irqc + ICP_IRQC_UNMASK_OFFSET; |
icp_hw_map.cmcr = hw_map(ICP_CMCR, PAGE_SIZE); |
icp_hw_map.sdramcr = icp_hw_map.cmcr + ICP_SDRAMCR_OFFSET; |
icp_hw_map.vga = hw_map(ICP_VGA, PAGE_SIZE); |
|
hw_map_init_called = true; |
} |
|
|
/** Acquire console back for kernel. */ |
void icp_grab_console(void) |
{ |
} |
|
/** Return console to userspace. */ |
void icp_release_console(void) |
{ |
} |
|
/** Starts icp Real Time Clock device, which asserts regular interrupts. |
* |
* @param frequency Interrupts frequency (0 disables RTC). |
*/ |
static void icp_timer_start(uint32_t frequency) |
{ |
icp_irqc_mask(ICP_TIMER_IRQ); |
*((uint32_t*) icp_hw_map.rtc1_load) = frequency; |
*((uint32_t*) icp_hw_map.rtc1_bgload) = frequency; |
*((uint32_t*) icp_hw_map.rtc1_ctl) = ICP_RTC_CTL_VALUE; |
icp_irqc_unmask(ICP_TIMER_IRQ); |
} |
|
static irq_ownership_t icp_timer_claim(irq_t *irq) |
{ |
if (icp_hw_map.rtc1_intrstat) { |
*((uint32_t*) icp_hw_map.rtc1_intrclr) = 1; |
return IRQ_ACCEPT; |
} else |
return IRQ_DECLINE; |
} |
|
/** Timer interrupt handler. |
* |
* @param irq Interrupt information. |
* @param arg Not used. |
*/ |
static void icp_timer_irq_handler(irq_t *irq) |
{ |
/* |
* We are holding a lock which prevents preemption. |
* Release the lock, call clock() and reacquire the lock again. |
*/ |
|
spinlock_unlock(&irq->lock); |
clock(); |
spinlock_lock(&irq->lock); |
|
} |
|
/** Initializes and registers timer interrupt handler. */ |
static void icp_timer_irq_init(void) |
{ |
irq_initialize(&icp_timer_irq); |
icp_timer_irq.devno = device_assign_devno(); |
icp_timer_irq.inr = ICP_TIMER_IRQ; |
icp_timer_irq.claim = icp_timer_claim; |
icp_timer_irq.handler = icp_timer_irq_handler; |
|
irq_register(&icp_timer_irq); |
} |
|
|
/** Starts timer. |
* |
* Initiates regular timer interrupts after initializing |
* corresponding interrupt handler. |
*/ |
void icp_timer_irq_start(void) |
{ |
icp_timer_irq_init(); |
icp_timer_start(ICP_TIMER_FREQ); |
} |
|
/** Returns the size of emulated memory. |
* |
* @return Size in bytes. |
*/ |
size_t icp_get_memory_size(void) |
{ |
if (hw_map_init_called) { |
return (sdram[((*(uint32_t *)icp_hw_map.sdramcr & ICP_SDRAM_MASK) >> 2)]); |
} else { |
return SDRAM_SIZE; |
} |
|
} |
|
/** Stops icp. */ |
void icp_cpu_halt(void) |
{ |
while (1); |
} |
|
/** interrupt exception handler. |
* |
* Determines sources of the interrupt from interrupt controller and |
* calls high-level handlers for them. |
* |
* @param exc_no Interrupt exception number. |
* @param istate Saved processor state. |
*/ |
void icp_irq_exception(int exc_no, istate_t *istate) |
{ |
uint32_t sources = icp_irqc_get_sources(); |
int i; |
|
for (i = 0; i < ICP_IRQC_MAX_IRQ; i++) { |
if (sources & (1 << i)) { |
irq_t *irq = irq_dispatch_and_lock(i); |
if (irq) { |
/* The IRQ handler was found. */ |
irq->handler(irq); |
spinlock_unlock(&irq->lock); |
} else { |
/* Spurious interrupt.*/ |
printf("cpu%d: spurious interrupt (inum=%d)\n", |
CPU->id, i); |
} |
} |
} |
} |
|
/** Returns address of framebuffer device. |
* |
* @return Address of framebuffer device. |
*/ |
uintptr_t icp_get_fb_address(void) |
{ |
if (!vga_init) { |
icp_vga_init(); |
vga_init = true; |
} |
return (uintptr_t) ICP_FB; |
} |
|
/* |
* Integrator specific frame initialization |
*/ |
void |
icp_frame_init(void) |
{ |
frame_mark_unavailable(ICP_FB_FRAME, ICP_FB_NUM_FRAME); |
frame_mark_unavailable(0, 256); |
} |
|
void icp_output_init(void) |
{ |
} |
|
void icp_input_init(void) |
{ |
|
pl050_t *pl050 = malloc(sizeof(pl050_t), FRAME_ATOMIC); |
pl050->status = (ioport8_t *)icp_hw_map.kbd_stat; |
pl050->data = (ioport8_t *)icp_hw_map.kbd_data; |
pl050->ctrl = (ioport8_t *)icp_hw_map.kbd_ctrl; |
|
pl050_instance_t *pl050_instance = pl050_init(pl050, ICP_KBD_IRQ); |
if (pl050_instance) { |
kbrd_instance_t *kbrd_instance = kbrd_init(); |
if (kbrd_instance) { |
icp_irqc_mask(ICP_KBD_IRQ); |
indev_t *sink = stdin_wire(); |
indev_t *kbrd = kbrd_wire(kbrd_instance, sink); |
pl050_wire(pl050_instance, kbrd); |
icp_irqc_unmask(ICP_KBD_IRQ); |
} |
} |
|
/* |
* This is the necessary evil until the userspace driver is entirely |
* self-sufficient. |
*/ |
sysinfo_set_item_val("kbd", NULL, true); |
sysinfo_set_item_val("kbd.inr", NULL, ICP_KBD_IRQ); |
sysinfo_set_item_val("kbd.address.status", NULL, |
(uintptr_t) icp_hw_map.kbd_stat); |
sysinfo_set_item_val("kbd.address.data", NULL, |
(uintptr_t) icp_hw_map.kbd_data); |
|
} |
|
|
/** @} |
*/ |