source: mainline/kernel/arch/arm32/src/mach/integratorcp/integratorcp.c

Last change on this file was b67ce1ff, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 years ago

Use irq_spinlock functions in arm32 code, to be consistent with all other code

  • Property mode set to 100644
File size: 9.5 KB
RevLine 
[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
60static struct {
61 icp_hw_map_t hw_map;
62 irq_t timer_irq;
[c882505]63 pl011_uart_t uart;
[8ec4144]64} icp;
65
[66fcba2]66struct 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
79static bool hw_map_init_called = false;
80uint32_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
91void icp_vga_init(void);
92
93/** Initializes the vga
94 *
95 */
96void 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. */
110static 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 */
119static 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 */
128static 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]134void 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 */
169static 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
178static 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 */
192static 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. */
206static 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 */
221void 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]232void 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. */
245void 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]259void 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 */
284void
285icp_frame_init(void)
286{
287 frame_mark_unavailable(ICP_FB_FRAME, ICP_FB_NUM_FRAME);
288 frame_mark_unavailable(0, 256);
289}
290
291void 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
319void 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]358size_t icp_get_irq_count(void)
359{
360 return ICP_IRQ_COUNT;
361}
[6ac14a70]362
[eff1f033]363const char *icp_get_platform_name(void)
364{
365 return "integratorcp";
366}
367
[6ac14a70]368/** @}
369 */
Note: See TracBrowser for help on using the repository browser.