2005-10-31 17:25:02 +03:00
/*
* linux / arch / arm / mach - realview / core . c
*
* Copyright ( C ) 1999 - 2003 ARM Limited
* Copyright ( C ) 2000 Deep Blue Solutions Ltd
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
2005-10-31 19:57:06 +03:00
# include <linux/platform_device.h>
2005-10-31 17:25:02 +03:00
# include <linux/dma-mapping.h>
# include <linux/sysdev.h>
# include <linux/interrupt.h>
2006-01-07 16:52:45 +03:00
# include <linux/amba/bus.h>
# include <linux/amba/clcd.h>
2008-02-04 19:24:54 +03:00
# include <linux/clocksource.h>
2008-02-04 19:26:55 +03:00
# include <linux/clockchips.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2009-01-20 16:23:30 +03:00
# include <linux/smsc911x.h>
2009-02-12 17:59:21 +03:00
# include <linux/ata_platform.h>
2009-09-22 17:29:36 +04:00
# include <linux/amba/mmci.h>
2005-10-31 17:25:02 +03:00
2008-11-08 23:05:55 +03:00
# include <asm/clkdev.h>
2005-10-31 17:25:02 +03:00
# include <asm/system.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-10-31 17:25:02 +03:00
# include <asm/irq.h>
# include <asm/leds.h>
2008-11-10 17:10:11 +03:00
# include <asm/mach-types.h>
2005-10-31 17:25:02 +03:00
# include <asm/hardware/arm_timer.h>
# include <asm/hardware/icst307.h>
# include <asm/mach/arch.h>
# include <asm/mach/flash.h>
# include <asm/mach/irq.h>
# include <asm/mach/map.h>
# include <asm/hardware/gic.h>
2009-05-30 17:00:17 +04:00
# include <mach/platform.h>
# include <mach/irqs.h>
2005-10-31 17:25:02 +03:00
# include "core.h"
# include "clock.h"
# define REALVIEW_REFCOUNTER (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_24MHz_OFFSET)
2008-12-01 17:54:58 +03:00
/* used by entry-macro.S and platsmp.c */
2008-02-04 19:41:01 +03:00
void __iomem * gic_cpu_base_addr ;
2009-11-04 15:19:05 +03:00
# ifdef CONFIG_ZONE_DMA
/*
* Adjust the zones if there are restrictions for DMA access .
*/
void __init realview_adjust_zones ( int node , unsigned long * size ,
unsigned long * hole )
{
unsigned long dma_size = SZ_256M > > PAGE_SHIFT ;
if ( ! machine_is_realview_pbx ( ) | | node | | ( size [ 0 ] < = dma_size ) )
return ;
size [ ZONE_NORMAL ] = size [ 0 ] - dma_size ;
size [ ZONE_DMA ] = dma_size ;
hole [ ZONE_NORMAL ] = hole [ 0 ] ;
hole [ ZONE_DMA ] = 0 ;
}
# endif
2005-10-31 17:25:02 +03:00
/*
* This is the RealView sched_clock implementation . This has
* a resolution of 41.7 ns , and a maximum value of about 179 s .
*/
unsigned long long sched_clock ( void )
{
unsigned long long v ;
v = ( unsigned long long ) readl ( REALVIEW_REFCOUNTER ) * 125 ;
do_div ( v , 3 ) ;
return v ;
}
# define REALVIEW_FLASHCTRL (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_FLASH_OFFSET)
static int realview_flash_init ( void )
{
u32 val ;
val = __raw_readl ( REALVIEW_FLASHCTRL ) ;
val & = ~ REALVIEW_FLASHPROG_FLVPPEN ;
__raw_writel ( val , REALVIEW_FLASHCTRL ) ;
return 0 ;
}
static void realview_flash_exit ( void )
{
u32 val ;
val = __raw_readl ( REALVIEW_FLASHCTRL ) ;
val & = ~ REALVIEW_FLASHPROG_FLVPPEN ;
__raw_writel ( val , REALVIEW_FLASHCTRL ) ;
}
static void realview_flash_set_vpp ( int on )
{
u32 val ;
val = __raw_readl ( REALVIEW_FLASHCTRL ) ;
if ( on )
val | = REALVIEW_FLASHPROG_FLVPPEN ;
else
val & = ~ REALVIEW_FLASHPROG_FLVPPEN ;
__raw_writel ( val , REALVIEW_FLASHCTRL ) ;
}
static struct flash_platform_data realview_flash_data = {
. map_name = " cfi_probe " ,
. width = 4 ,
. init = realview_flash_init ,
. exit = realview_flash_exit ,
. set_vpp = realview_flash_set_vpp ,
} ;
struct platform_device realview_flash_device = {
. name = " armflash " ,
. id = 0 ,
. dev = {
. platform_data = & realview_flash_data ,
} ,
} ;
2008-04-19 01:43:10 +04:00
int realview_flash_register ( struct resource * res , u32 num )
{
realview_flash_device . resource = res ;
realview_flash_device . num_resources = num ;
return platform_device_register ( & realview_flash_device ) ;
}
2009-01-20 16:23:30 +03:00
static struct smsc911x_platform_config smsc911x_config = {
. flags = SMSC911X_USE_32BIT ,
. irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH ,
. irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL ,
. phy_interface = PHY_INTERFACE_MODE_MII ,
2008-12-01 17:54:59 +03:00
} ;
2008-12-01 17:54:58 +03:00
static struct platform_device realview_eth_device = {
2009-01-20 16:23:30 +03:00
. name = " smsc911x " ,
2008-12-01 17:54:58 +03:00
. id = 0 ,
. num_resources = 2 ,
} ;
int realview_eth_register ( const char * name , struct resource * res )
{
if ( name )
realview_eth_device . name = name ;
realview_eth_device . resource = res ;
2009-01-20 16:23:30 +03:00
if ( strcmp ( realview_eth_device . name , " smsc911x " ) = = 0 )
realview_eth_device . dev . platform_data = & smsc911x_config ;
2008-12-01 17:54:58 +03:00
return platform_device_register ( & realview_eth_device ) ;
2009-02-12 18:00:21 +03:00
}
struct platform_device realview_usb_device = {
. name = " isp1760 " ,
. num_resources = 2 ,
} ;
int realview_usb_register ( struct resource * res )
{
realview_usb_device . resource = res ;
return platform_device_register ( & realview_usb_device ) ;
2008-12-01 17:54:58 +03:00
}
2009-02-12 17:59:21 +03:00
static struct pata_platform_info pata_platform_data = {
. ioport_shift = 1 ,
} ;
static struct resource pata_resources [ ] = {
[ 0 ] = {
. start = REALVIEW_CF_BASE ,
. end = REALVIEW_CF_BASE + 0xff ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. start = REALVIEW_CF_BASE + 0x100 ,
. end = REALVIEW_CF_BASE + SZ_4K - 1 ,
. flags = IORESOURCE_MEM ,
} ,
} ;
struct platform_device realview_cf_device = {
. name = " pata_platform " ,
. id = - 1 ,
. num_resources = ARRAY_SIZE ( pata_resources ) ,
. resource = pata_resources ,
. dev = {
. platform_data = & pata_platform_data ,
} ,
} ;
2006-12-10 23:21:32 +03:00
static struct resource realview_i2c_resource = {
. start = REALVIEW_I2C_BASE ,
. end = REALVIEW_I2C_BASE + SZ_4K - 1 ,
. flags = IORESOURCE_MEM ,
} ;
struct platform_device realview_i2c_device = {
. name = " versatile-i2c " ,
2009-02-12 17:58:20 +03:00
. id = 0 ,
2006-12-10 23:21:32 +03:00
. num_resources = 1 ,
. resource = & realview_i2c_resource ,
} ;
2009-02-12 17:58:20 +03:00
static struct i2c_board_info realview_i2c_board_info [ ] = {
{
2009-07-18 18:51:55 +04:00
I2C_BOARD_INFO ( " ds1338 " , 0xd0 > > 1 ) ,
2009-02-12 17:58:20 +03:00
} ,
} ;
static int __init realview_i2c_init ( void )
{
return i2c_register_board_info ( 0 , realview_i2c_board_info ,
ARRAY_SIZE ( realview_i2c_board_info ) ) ;
}
arch_initcall ( realview_i2c_init ) ;
2005-10-31 17:25:02 +03:00
# define REALVIEW_SYSMCI (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_MCI_OFFSET)
2009-07-09 18:17:41 +04:00
/*
* This is only used if GPIOLIB support is disabled
*/
2005-10-31 17:25:02 +03:00
static unsigned int realview_mmc_status ( struct device * dev )
{
struct amba_device * adev = container_of ( dev , struct amba_device , dev ) ;
u32 mask ;
if ( adev - > res . start = = REALVIEW_MMCI0_BASE )
mask = 1 ;
else
mask = 2 ;
return readl ( REALVIEW_SYSMCI ) & mask ;
}
2009-09-22 17:29:36 +04:00
struct mmci_platform_data realview_mmc0_plat_data = {
2005-10-31 17:25:02 +03:00
. ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34 ,
. status = realview_mmc_status ,
2009-07-09 18:17:41 +04:00
. gpio_wp = 17 ,
. gpio_cd = 16 ,
2005-10-31 17:25:02 +03:00
} ;
2009-09-22 17:29:36 +04:00
struct mmci_platform_data realview_mmc1_plat_data = {
2005-10-31 17:25:02 +03:00
. ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34 ,
. status = realview_mmc_status ,
2009-07-09 18:17:41 +04:00
. gpio_wp = 19 ,
. gpio_cd = 18 ,
2005-10-31 17:25:02 +03:00
} ;
/*
* Clock handling
*/
2010-01-16 19:27:28 +03:00
static const struct icst_params realview_oscvco_params = {
2005-10-31 17:25:02 +03:00
. ref = 24000 ,
. vco_max = 200000 ,
. vd_min = 4 + 8 ,
. vd_max = 511 + 8 ,
. rd_min = 1 + 2 ,
. rd_max = 127 + 2 ,
} ;
2010-01-16 19:27:28 +03:00
static void realview_oscvco_set ( struct clk * clk , struct icst_vco vco )
2005-10-31 17:25:02 +03:00
{
void __iomem * sys_lock = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_LOCK_OFFSET ;
2008-11-10 17:10:11 +03:00
void __iomem * sys_osc ;
2005-10-31 17:25:02 +03:00
u32 val ;
2008-11-10 17:10:11 +03:00
if ( machine_is_realview_pb1176 ( ) )
sys_osc = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_OSC0_OFFSET ;
else
sys_osc = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_OSC4_OFFSET ;
2005-10-31 17:25:02 +03:00
val = readl ( sys_osc ) & ~ 0x7ffff ;
val | = vco . v | ( vco . r < < 9 ) | ( vco . s < < 16 ) ;
writel ( 0xa05f , sys_lock ) ;
writel ( val , sys_osc ) ;
writel ( 0 , sys_lock ) ;
}
2008-11-08 23:05:55 +03:00
static struct clk oscvco_clk = {
2005-10-31 17:25:02 +03:00
. params = & realview_oscvco_params ,
. setvco = realview_oscvco_set ,
} ;
2008-11-08 23:05:55 +03:00
/*
* These are fixed clocks .
*/
static struct clk ref24_clk = {
. rate = 24000000 ,
} ;
static struct clk_lookup lookups [ ] = {
{ /* UART0 */
2009-09-21 15:30:32 +04:00
. dev_id = " dev:uart0 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* UART1 */
2009-09-21 15:30:32 +04:00
. dev_id = " dev:uart1 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* UART2 */
2009-09-21 15:30:32 +04:00
. dev_id = " dev:uart2 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* UART3 */
2009-09-21 15:30:32 +04:00
. dev_id = " fpga:uart3 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* KMI0 */
2009-09-21 15:30:32 +04:00
. dev_id = " fpga:kmi0 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* KMI1 */
2009-09-21 15:30:32 +04:00
. dev_id = " fpga:kmi1 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* MMC0 */
2009-09-21 15:30:32 +04:00
. dev_id = " fpga:mmc0 " ,
2008-11-08 23:05:55 +03:00
. clk = & ref24_clk ,
} , { /* EB:CLCD */
2009-09-21 15:30:32 +04:00
. dev_id = " dev:clcd " ,
2008-11-08 23:05:55 +03:00
. clk = & oscvco_clk ,
} , { /* PB:CLCD */
2009-09-21 15:30:32 +04:00
. dev_id = " issp:clcd " ,
2008-11-08 23:05:55 +03:00
. clk = & oscvco_clk ,
}
} ;
static int __init clk_init ( void )
{
2010-01-12 15:28:00 +03:00
clkdev_add_table ( lookups , ARRAY_SIZE ( lookups ) ) ;
2008-11-08 23:05:55 +03:00
return 0 ;
}
arch_initcall ( clk_init ) ;
2005-10-31 17:25:02 +03:00
/*
* CLCD support .
*/
# define SYS_CLCD_NLCDIOON (1 << 2)
# define SYS_CLCD_VDDPOSSWITCH (1 << 3)
# define SYS_CLCD_PWR3V5SWITCH (1 << 4)
# define SYS_CLCD_ID_MASK (0x1f << 8)
# define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
# define SYS_CLCD_ID_UNKNOWN_8_4 (0x01 << 8)
# define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
# define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
# define SYS_CLCD_ID_VGA (0x1f << 8)
static struct clcd_panel vga = {
. mode = {
. name = " VGA " ,
. refresh = 60 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 39721 ,
. left_margin = 40 ,
. right_margin = 24 ,
. upper_margin = 32 ,
. lower_margin = 11 ,
. hsync_len = 96 ,
. vsync_len = 2 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ,
. width = - 1 ,
. height = - 1 ,
. tim2 = TIM2_BCD | TIM2_IPC ,
2008-11-10 17:10:13 +03:00
. cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP ( 1 ) ,
2005-10-31 17:25:02 +03:00
. bpp = 16 ,
} ;
2008-11-10 17:10:12 +03:00
static struct clcd_panel xvga = {
. mode = {
. name = " XVGA " ,
. refresh = 60 ,
. xres = 1024 ,
. yres = 768 ,
. pixclock = 15748 ,
. left_margin = 152 ,
. right_margin = 48 ,
. upper_margin = 23 ,
. lower_margin = 3 ,
. hsync_len = 104 ,
. vsync_len = 4 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ,
. width = - 1 ,
. height = - 1 ,
. tim2 = TIM2_BCD | TIM2_IPC ,
2008-11-10 17:10:13 +03:00
. cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP ( 1 ) ,
2005-10-31 17:25:02 +03:00
. bpp = 16 ,
} ;
static struct clcd_panel sanyo_3_8_in = {
. mode = {
. name = " Sanyo QVGA " ,
. refresh = 116 ,
. xres = 320 ,
. yres = 240 ,
. pixclock = 100000 ,
. left_margin = 6 ,
. right_margin = 6 ,
. upper_margin = 5 ,
. lower_margin = 5 ,
. hsync_len = 6 ,
. vsync_len = 6 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ,
. width = - 1 ,
. height = - 1 ,
. tim2 = TIM2_BCD ,
2008-11-10 17:10:13 +03:00
. cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP ( 1 ) ,
2005-10-31 17:25:02 +03:00
. bpp = 16 ,
} ;
static struct clcd_panel sanyo_2_5_in = {
. mode = {
. name = " Sanyo QVGA Portrait " ,
. refresh = 116 ,
. xres = 240 ,
. yres = 320 ,
. pixclock = 100000 ,
. left_margin = 20 ,
. right_margin = 10 ,
. upper_margin = 2 ,
. lower_margin = 2 ,
. hsync_len = 10 ,
. vsync_len = 2 ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
. vmode = FB_VMODE_NONINTERLACED ,
} ,
. width = - 1 ,
. height = - 1 ,
. tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC ,
2008-11-10 17:10:13 +03:00
. cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP ( 1 ) ,
2005-10-31 17:25:02 +03:00
. bpp = 16 ,
} ;
static struct clcd_panel epson_2_2_in = {
. mode = {
. name = " Epson QCIF " ,
. refresh = 390 ,
. xres = 176 ,
. yres = 220 ,
. pixclock = 62500 ,
. left_margin = 3 ,
. right_margin = 2 ,
. upper_margin = 1 ,
. lower_margin = 0 ,
. hsync_len = 3 ,
. vsync_len = 2 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ,
. width = - 1 ,
. height = - 1 ,
. tim2 = TIM2_BCD | TIM2_IPC ,
2008-11-10 17:10:13 +03:00
. cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP ( 1 ) ,
2005-10-31 17:25:02 +03:00
. bpp = 16 ,
} ;
/*
* Detect which LCD panel is connected , and return the appropriate
* clcd_panel structure . Note : we do not have any information on
* the required timings for the 8.4 in panel , so we presently assume
* VGA timings .
*/
static struct clcd_panel * realview_clcd_panel ( void )
{
void __iomem * sys_clcd = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_CLCD_OFFSET ;
2008-11-10 17:10:12 +03:00
struct clcd_panel * vga_panel ;
struct clcd_panel * panel ;
2005-10-31 17:25:02 +03:00
u32 val ;
2008-11-10 17:10:12 +03:00
if ( machine_is_realview_eb ( ) )
vga_panel = & vga ;
else
vga_panel = & xvga ;
2005-10-31 17:25:02 +03:00
val = readl ( sys_clcd ) & SYS_CLCD_ID_MASK ;
if ( val = = SYS_CLCD_ID_SANYO_3_8 )
panel = & sanyo_3_8_in ;
else if ( val = = SYS_CLCD_ID_SANYO_2_5 )
panel = & sanyo_2_5_in ;
else if ( val = = SYS_CLCD_ID_EPSON_2_2 )
panel = & epson_2_2_in ;
else if ( val = = SYS_CLCD_ID_VGA )
2008-11-10 17:10:12 +03:00
panel = vga_panel ;
2005-10-31 17:25:02 +03:00
else {
printk ( KERN_ERR " CLCD: unknown LCD panel ID 0x%08x, using VGA \n " ,
val ) ;
2008-11-10 17:10:12 +03:00
panel = vga_panel ;
2005-10-31 17:25:02 +03:00
}
return panel ;
}
/*
* Disable all display connectors on the interface module .
*/
static void realview_clcd_disable ( struct clcd_fb * fb )
{
void __iomem * sys_clcd = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_CLCD_OFFSET ;
u32 val ;
val = readl ( sys_clcd ) ;
val & = ~ SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH ;
writel ( val , sys_clcd ) ;
}
/*
* Enable the relevant connector on the interface module .
*/
static void realview_clcd_enable ( struct clcd_fb * fb )
{
void __iomem * sys_clcd = __io_address ( REALVIEW_SYS_BASE ) + REALVIEW_SYS_CLCD_OFFSET ;
u32 val ;
/*
2006-03-16 17:10:20 +03:00
* Enable the PSUs
2005-10-31 17:25:02 +03:00
*/
2006-03-16 17:10:20 +03:00
val = readl ( sys_clcd ) ;
2005-10-31 17:25:02 +03:00
val | = SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH ;
writel ( val , sys_clcd ) ;
}
static int realview_clcd_setup ( struct clcd_fb * fb )
{
2008-11-10 17:10:12 +03:00
unsigned long framesize ;
2005-10-31 17:25:02 +03:00
dma_addr_t dma ;
2008-11-10 17:10:12 +03:00
if ( machine_is_realview_eb ( ) )
/* VGA, 16bpp */
framesize = 640 * 480 * 2 ;
else
/* XVGA, 16bpp */
framesize = 1024 * 768 * 2 ;
2005-10-31 17:25:02 +03:00
fb - > panel = realview_clcd_panel ( ) ;
fb - > fb . screen_base = dma_alloc_writecombine ( & fb - > dev - > dev , framesize ,
2009-11-04 15:19:05 +03:00
& dma , GFP_KERNEL | GFP_DMA ) ;
2005-10-31 17:25:02 +03:00
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 = framesize ;
return 0 ;
}
static int realview_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 realview_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 ) ;
}
struct clcd_board clcd_plat_data = {
. name = " RealView " ,
. check = clcdfb_check ,
. decode = clcdfb_decode ,
. disable = realview_clcd_disable ,
. enable = realview_clcd_enable ,
. setup = realview_clcd_setup ,
. mmap = realview_clcd_mmap ,
. remove = realview_clcd_remove ,
} ;
# ifdef CONFIG_LEDS
# define VA_LEDS_BASE (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LED_OFFSET)
void realview_leds_event ( led_event_t ledevt )
{
unsigned long flags ;
u32 val ;
2009-05-30 16:56:16 +04:00
u32 led = 1 < < smp_processor_id ( ) ;
2005-10-31 17:25:02 +03:00
local_irq_save ( flags ) ;
val = readl ( VA_LEDS_BASE ) ;
switch ( ledevt ) {
case led_idle_start :
2009-05-30 16:56:16 +04:00
val = val & ~ led ;
2005-10-31 17:25:02 +03:00
break ;
case led_idle_end :
2009-05-30 16:56:16 +04:00
val = val | led ;
2005-10-31 17:25:02 +03:00
break ;
case led_timer :
2009-05-30 16:56:16 +04:00
val = val ^ REALVIEW_SYS_LED7 ;
2005-10-31 17:25:02 +03:00
break ;
case led_halted :
val = 0 ;
break ;
default :
break ;
}
writel ( val , VA_LEDS_BASE ) ;
local_irq_restore ( flags ) ;
}
# endif /* CONFIG_LEDS */
/*
* Where is the timer ( VA ) ?
*/
2008-04-19 01:43:11 +04:00
void __iomem * timer0_va_base ;
void __iomem * timer1_va_base ;
void __iomem * timer2_va_base ;
void __iomem * timer3_va_base ;
2005-10-31 17:25:02 +03:00
/*
* How long is the timer interval ?
*/
# define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
# if TIMER_INTERVAL >= 0x100000
# define TIMER_RELOAD (TIMER_INTERVAL >> 8)
# define TIMER_DIVISOR (TIMER_CTRL_DIV256)
# define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
# elif TIMER_INTERVAL >= 0x10000
# define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */
# define TIMER_DIVISOR (TIMER_CTRL_DIV16)
# define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
# else
# define TIMER_RELOAD (TIMER_INTERVAL)
# define TIMER_DIVISOR (TIMER_CTRL_DIV1)
# define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
# endif
2008-02-04 19:26:55 +03:00
static void timer_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * clk )
{
unsigned long ctrl ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2008-04-19 01:43:11 +04:00
writel ( TIMER_RELOAD , timer0_va_base + TIMER_LOAD ) ;
2008-02-04 19:26:55 +03:00
ctrl = TIMER_CTRL_PERIODIC ;
ctrl | = TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
/* period set, and timer enabled in 'next_event' hook */
ctrl = TIMER_CTRL_ONESHOT ;
ctrl | = TIMER_CTRL_32BIT | TIMER_CTRL_IE ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
default :
ctrl = 0 ;
}
2008-04-19 01:43:11 +04:00
writel ( ctrl , timer0_va_base + TIMER_CTRL ) ;
2008-02-04 19:26:55 +03:00
}
static int timer_set_next_event ( unsigned long evt ,
struct clock_event_device * unused )
{
2008-04-19 01:43:11 +04:00
unsigned long ctrl = readl ( timer0_va_base + TIMER_CTRL ) ;
2008-02-04 19:26:55 +03:00
2008-04-19 01:43:11 +04:00
writel ( evt , timer0_va_base + TIMER_LOAD ) ;
writel ( ctrl | TIMER_CTRL_ENABLE , timer0_va_base + TIMER_CTRL ) ;
2008-02-04 19:26:55 +03:00
return 0 ;
}
static struct clock_event_device timer0_clockevent = {
. name = " timer0 " ,
. shift = 32 ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. set_mode = timer_set_mode ,
. set_next_event = timer_set_next_event ,
. rating = 300 ,
2008-12-13 13:50:26 +03:00
. cpumask = cpu_all_mask ,
2008-02-04 19:26:55 +03:00
} ;
2008-02-04 19:43:02 +03:00
static void __init realview_clockevents_init ( unsigned int timer_irq )
2008-02-04 19:26:55 +03:00
{
2008-02-04 19:43:02 +03:00
timer0_clockevent . irq = timer_irq ;
2008-02-04 19:26:55 +03:00
timer0_clockevent . mult =
div_sc ( 1000000 , NSEC_PER_SEC , timer0_clockevent . shift ) ;
timer0_clockevent . max_delta_ns =
clockevent_delta2ns ( 0xffffffff , & timer0_clockevent ) ;
timer0_clockevent . min_delta_ns =
clockevent_delta2ns ( 0xf , & timer0_clockevent ) ;
clockevents_register_device ( & timer0_clockevent ) ;
}
2005-10-31 17:25:02 +03:00
/*
* IRQ handler for the timer
*/
2006-10-06 21:53:39 +04:00
static irqreturn_t realview_timer_interrupt ( int irq , void * dev_id )
2005-10-31 17:25:02 +03:00
{
2008-02-04 19:26:55 +03:00
struct clock_event_device * evt = & timer0_clockevent ;
2005-10-31 17:25:02 +03:00
2008-02-04 19:26:55 +03:00
/* clear the interrupt */
2008-04-19 01:43:11 +04:00
writel ( 1 , timer0_va_base + TIMER_INTCLR ) ;
2005-10-31 17:25:02 +03:00
2008-02-04 19:26:55 +03:00
evt - > event_handler ( evt ) ;
2005-11-08 13:40:10 +03:00
2005-10-31 17:25:02 +03:00
return IRQ_HANDLED ;
}
static struct irqaction realview_timer_irq = {
. name = " RealView Timer Tick " ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2005-10-31 17:25:02 +03:00
. handler = realview_timer_interrupt ,
} ;
2009-04-21 23:24:00 +04:00
static cycle_t realview_get_cycles ( struct clocksource * cs )
2008-02-04 19:24:54 +03:00
{
2008-04-19 01:43:11 +04:00
return ~ readl ( timer3_va_base + TIMER_VALUE ) ;
2008-02-04 19:24:54 +03:00
}
static struct clocksource clocksource_realview = {
. name = " timer3 " ,
. rating = 200 ,
. read = realview_get_cycles ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 20 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static void __init realview_clocksource_init ( void )
{
/* setup timer 0 as free-running clocksource */
2008-04-19 01:43:11 +04:00
writel ( 0 , timer3_va_base + TIMER_CTRL ) ;
writel ( 0xffffffff , timer3_va_base + TIMER_LOAD ) ;
writel ( 0xffffffff , timer3_va_base + TIMER_VALUE ) ;
2008-02-04 19:24:54 +03:00
writel ( TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC ,
2008-04-19 01:43:11 +04:00
timer3_va_base + TIMER_CTRL ) ;
2008-02-04 19:24:54 +03:00
clocksource_realview . mult =
clocksource_khz2mult ( 1000 , clocksource_realview . shift ) ;
clocksource_register ( & clocksource_realview ) ;
}
2005-10-31 17:25:02 +03:00
/*
2008-02-04 19:30:57 +03:00
* Set up the clock source and clock events devices
2005-10-31 17:25:02 +03:00
*/
2008-02-04 19:43:02 +03:00
void __init realview_timer_init ( unsigned int timer_irq )
2005-10-31 17:25:02 +03:00
{
u32 val ;
/*
* set clock frequency :
* REALVIEW_REFCLK is 32 KHz
* REALVIEW_TIMCLK is 1 MHz
*/
val = readl ( __io_address ( REALVIEW_SCTL_BASE ) ) ;
writel ( ( REALVIEW_TIMCLK < < REALVIEW_TIMER1_EnSel ) |
( REALVIEW_TIMCLK < < REALVIEW_TIMER2_EnSel ) |
( REALVIEW_TIMCLK < < REALVIEW_TIMER3_EnSel ) |
( REALVIEW_TIMCLK < < REALVIEW_TIMER4_EnSel ) | val ,
__io_address ( REALVIEW_SCTL_BASE ) ) ;
/*
* Initialise to a known state ( all timers off )
*/
2008-04-19 01:43:11 +04:00
writel ( 0 , timer0_va_base + TIMER_CTRL ) ;
writel ( 0 , timer1_va_base + TIMER_CTRL ) ;
writel ( 0 , timer2_va_base + TIMER_CTRL ) ;
writel ( 0 , timer3_va_base + TIMER_CTRL ) ;
2005-10-31 17:25:02 +03:00
/*
* Make irqs happen for the system timer
*/
2008-02-04 19:43:02 +03:00
setup_irq ( timer_irq , & realview_timer_irq ) ;
2008-02-04 19:24:54 +03:00
realview_clocksource_init ( ) ;
2008-02-04 19:43:02 +03:00
realview_clockevents_init ( timer_irq ) ;
2005-10-31 17:25:02 +03:00
}
2009-11-04 15:19:04 +03:00
/*
* Setup the memory banks .
*/
void realview_fixup ( struct machine_desc * mdesc , struct tag * tags , char * * from ,
struct meminfo * meminfo )
{
/*
* Most RealView platforms have 512 MB contiguous RAM at 0x70000000 .
* Half of this is mirrored at 0.
*/
# ifdef CONFIG_REALVIEW_HIGH_PHYS_OFFSET
meminfo - > bank [ 0 ] . start = 0x70000000 ;
meminfo - > bank [ 0 ] . size = SZ_512M ;
meminfo - > nr_banks = 1 ;
# else
meminfo - > bank [ 0 ] . start = 0 ;
meminfo - > bank [ 0 ] . size = SZ_256M ;
meminfo - > nr_banks = 1 ;
# endif
}