2005-06-20 21:51:05 +04:00
/*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-06-20 21:51:05 +04:00
# include <linux/list.h>
# include <linux/errno.h>
2005-10-28 19:51:44 +04:00
# include <linux/dma-mapping.h>
2005-06-20 21:51:05 +04:00
# include <linux/interrupt.h>
# include <linux/timex.h>
# include <linux/signal.h>
2008-08-04 13:41:28 +04:00
# include <asm/arch/hardware.h>
2005-06-20 21:51:05 +04:00
# include <asm/irq.h>
2005-10-28 19:51:44 +04:00
# include <asm/sizes.h>
2005-06-20 21:51:05 +04:00
2005-10-28 19:51:42 +04:00
# include <asm/mach/flash.h>
2005-06-20 21:51:05 +04:00
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
# include <asm/mach/map.h>
2005-10-28 19:51:44 +04:00
# include "core.h"
# include "clock.h"
2005-06-20 21:51:05 +04:00
/*
* 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 = {
2005-10-28 18:18:56 +04:00
{
. virtual = VIO_APB_BASE ,
2006-05-08 01:49:21 +04:00
. pfn = __phys_to_pfn ( PIO_APB_BASE ) ,
2005-10-28 18:18:56 +04:00
. length = IO_APB_LENGTH ,
. type = MT_DEVICE
} , {
. virtual = VIO_AHB_BASE ,
2006-05-08 01:49:21 +04:00
. pfn = __phys_to_pfn ( PIO_AHB_BASE ) ,
2005-10-28 18:18:56 +04:00
. length = IO_AHB_LENGTH ,
. type = MT_DEVICE
}
2005-06-20 21:51:05 +04:00
} ;
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 ) ;
}
2006-11-23 14:41:32 +03:00
static struct irq_chip aaec2000_irq_chip = {
2005-06-20 21:51:05 +04:00
. 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 + + ) {
2006-11-23 14:41:32 +03:00
set_irq_handler ( i , handle_level_irq ) ;
2005-06-20 21:51:05 +04:00
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_t
2006-10-06 21:53:39 +04:00
aaec2000_timer_interrupt ( int irq , void * dev_id )
2005-06-20 21:51:05 +04:00
{
/* TODO: Check timer accuracy */
2006-10-06 21:53:39 +04:00
timer_tick ( ) ;
2005-06-20 21:51:05 +04:00
TIMER1_CLEAR = 1 ;
return IRQ_HANDLED ;
}
static struct irqaction aaec2000_timer_irq = {
. name = " AAEC-2000 Timer Tick " ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2005-06-26 20:06:36 +04:00
. handler = aaec2000_timer_interrupt ,
2005-06-20 21:51:05 +04:00
} ;
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 508 KHz 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 ,
} ;
2005-10-28 19:51:44 +04:00
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 ) ) ;
}
2005-10-28 19:51:42 +04:00
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 )
{
2005-10-28 19:51:44 +04:00
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 ) ;
}
2005-10-28 19:51:42 +04:00
platform_device_register ( & aaec2000_flash_device ) ;
return 0 ;
} ;
arch_initcall ( aaec2000_init ) ;