📄 core.c
字号:
/* * linux/arch/arm/mach-aaec2000/core.c * * Code common to all AAEC-2000 machines * * Copyright (c) 2005 Nicolas Bellido Y Ortega * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/platform_device.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <linux/timex.h>#include <linux/signal.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/sizes.h>#include <asm/hardware/amba.h>#include <asm/mach/flash.h>#include <asm/mach/irq.h>#include <asm/mach/time.h>#include <asm/mach/map.h>#include "core.h"#include "clock.h"/* * Common I/O mapping: * * Static virtual address mappings are as follow: * * 0xf8000000-0xf8001ffff: Devices connected to APB bus * 0xf8002000-0xf8003ffff: Devices connected to AHB bus * * Below 0xe8000000 is reserved for vm allocation. * * The machine specific code must provide the extra mapping beside the * default mapping provided here. */static struct map_desc standard_io_desc[] __initdata = { { .virtual = VIO_APB_BASE, .physical = __phys_to_pfn(PIO_APB_BASE), .length = IO_APB_LENGTH, .type = MT_DEVICE }, { .virtual = VIO_AHB_BASE, .physical = __phys_to_pfn(PIO_AHB_BASE), .length = IO_AHB_LENGTH, .type = MT_DEVICE }};void __init aaec2000_map_io(void){ iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));}/* * Interrupt handling routines */static void aaec2000_int_ack(unsigned int irq){ IRQ_INTSR = 1 << irq;}static void aaec2000_int_mask(unsigned int irq){ IRQ_INTENC |= (1 << irq);}static void aaec2000_int_unmask(unsigned int irq){ IRQ_INTENS |= (1 << irq);}static struct irqchip aaec2000_irq_chip = { .ack = aaec2000_int_ack, .mask = aaec2000_int_mask, .unmask = aaec2000_int_unmask,};void __init aaec2000_init_irq(void){ unsigned int i; for (i = 0; i < NR_IRQS; i++) { set_irq_handler(i, do_level_IRQ); set_irq_chip(i, &aaec2000_irq_chip); set_irq_flags(i, IRQF_VALID); } /* Disable all interrupts */ IRQ_INTENC = 0xffffffff; /* Clear any pending interrupts */ IRQ_INTSR = IRQ_INTSR;}/* * Time keeping *//* IRQs are disabled before entering here from do_gettimeofday() */static unsigned long aaec2000_gettimeoffset(void){ unsigned long ticks_to_match, elapsed, usec; /* Get ticks before next timer match */ ticks_to_match = TIMER1_LOAD - TIMER1_VAL; /* We need elapsed ticks since last match */ elapsed = LATCH - ticks_to_match; /* Now, convert them to usec */ usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; return usec;}/* We enter here with IRQs enabled */static irqreturn_taaec2000_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs){ /* TODO: Check timer accuracy */ write_seqlock(&xtime_lock); timer_tick(regs); TIMER1_CLEAR = 1; write_sequnlock(&xtime_lock); return IRQ_HANDLED;}static struct irqaction aaec2000_timer_irq = { .name = "AAEC-2000 Timer Tick", .flags = SA_INTERRUPT | SA_TIMER, .handler = aaec2000_timer_interrupt,};static void __init aaec2000_timer_init(void){ /* Disable timer 1 */ TIMER1_CTRL = 0; /* We have somehow to generate a 100Hz clock. * We then use the 508KHz timer in periodic mode. */ TIMER1_LOAD = LATCH; TIMER1_CLEAR = 1; /* Clear interrupt */ setup_irq(INT_TMR1_OFL, &aaec2000_timer_irq); TIMER1_CTRL = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC | TIMER_CTRL_CLKSEL_508K;}struct sys_timer aaec2000_timer = { .init = aaec2000_timer_init, .offset = aaec2000_gettimeoffset,};static struct clcd_panel mach_clcd_panel;static int aaec2000_clcd_setup(struct clcd_fb *fb){ dma_addr_t dma; fb->panel = &mach_clcd_panel; fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, SZ_1M, &dma, GFP_KERNEL); if (!fb->fb.screen_base) { printk(KERN_ERR "CLCD: unable to map framebuffer\n"); return -ENOMEM; } fb->fb.fix.smem_start = dma; fb->fb.fix.smem_len = SZ_1M; return 0;}static int aaec2000_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma){ return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base, fb->fb.fix.smem_start, fb->fb.fix.smem_len);}static void aaec2000_clcd_remove(struct clcd_fb *fb){ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base, fb->fb.fix.smem_start);}static struct clcd_board clcd_plat_data = { .name = "AAEC-2000", .check = clcdfb_check, .decode = clcdfb_decode, .setup = aaec2000_clcd_setup, .mmap = aaec2000_clcd_mmap, .remove = aaec2000_clcd_remove,};static struct amba_device clcd_device = { .dev = { .bus_id = "mb:16", .coherent_dma_mask = ~0, .platform_data = &clcd_plat_data, }, .res = { .start = AAEC_CLCD_PHYS, .end = AAEC_CLCD_PHYS + SZ_4K - 1, .flags = IORESOURCE_MEM, }, .irq = { INT_LCD, NO_IRQ }, .periphid = 0x41110,};static struct amba_device *amba_devs[] __initdata = { &clcd_device,};static struct clk aaec2000_clcd_clk = { .name = "CLCDCLK",};void __init aaec2000_set_clcd_plat_data(struct aaec2000_clcd_info *clcd){ clcd_plat_data.enable = clcd->enable; clcd_plat_data.disable = clcd->disable; memcpy(&mach_clcd_panel, &clcd->panel, sizeof(struct clcd_panel));}static struct flash_platform_data aaec2000_flash_data = { .map_name = "cfi_probe", .width = 4,};static struct resource aaec2000_flash_resource = { .start = AAEC_FLASH_BASE, .end = AAEC_FLASH_BASE + AAEC_FLASH_SIZE, .flags = IORESOURCE_MEM,};static struct platform_device aaec2000_flash_device = { .name = "armflash", .id = 0, .dev = { .platform_data = &aaec2000_flash_data, }, .num_resources = 1, .resource = &aaec2000_flash_resource,};static int __init aaec2000_init(void){ int i; clk_register(&aaec2000_clcd_clk); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; amba_device_register(d, &iomem_resource); } platform_device_register(&aaec2000_flash_device); return 0;};arch_initcall(aaec2000_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -