2005-07-10 22:58:15 +04:00
/*
* linux / arch / arm / plat - omap / common . c
*
* Code common to all OMAP machines .
*
* 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>
# include <linux/delay.h>
# include <linux/pm.h>
# include <linux/console.h>
# include <linux/serial.h>
# include <linux/tty.h>
# include <linux/serial_8250.h>
# include <linux/serial_reg.h>
2006-01-07 19:15:52 +03:00
# include <linux/clk.h>
2005-07-10 22:58:15 +04:00
# include <asm/hardware.h>
# include <asm/system.h>
# include <asm/pgtable.h>
# include <asm/mach/map.h>
# include <asm/io.h>
2005-09-07 20:20:26 +04:00
# include <asm/setup.h>
2005-07-10 22:58:15 +04:00
# include <asm/arch/board.h>
# include <asm/arch/mux.h>
# include <asm/arch/fpga.h>
2005-11-10 17:26:50 +03:00
# include <asm/arch/clock.h>
2005-07-10 22:58:15 +04:00
# define NO_LENGTH_CHECK 0xffffffff
2005-09-07 20:20:26 +04:00
unsigned char omap_bootloader_tag [ 512 ] ;
int omap_bootloader_tag_len ;
2005-07-10 22:58:15 +04:00
struct omap_board_config_kernel * omap_board_config ;
2005-09-07 20:20:26 +04:00
int omap_board_config_size ;
2005-07-10 22:58:15 +04:00
static const void * get_config ( u16 tag , size_t len , int skip , size_t * len_out )
{
struct omap_board_config_kernel * kinfo = NULL ;
int i ;
# ifdef CONFIG_OMAP_BOOT_TAG
struct omap_board_config_entry * info = NULL ;
if ( omap_bootloader_tag_len > 4 )
info = ( struct omap_board_config_entry * ) omap_bootloader_tag ;
while ( info ! = NULL ) {
u8 * next ;
if ( info - > tag = = tag ) {
if ( skip = = 0 )
break ;
skip - - ;
}
if ( ( info - > len & 0x03 ) ! = 0 ) {
/* We bail out to avoid an alignment fault */
printk ( KERN_ERR " OMAP peripheral config: Length (%d) not word-aligned (tag %04x) \n " ,
info - > len , info - > tag ) ;
return NULL ;
}
next = ( u8 * ) info + sizeof ( * info ) + info - > len ;
if ( next > = omap_bootloader_tag + omap_bootloader_tag_len )
info = NULL ;
else
info = ( struct omap_board_config_entry * ) next ;
}
if ( info ! = NULL ) {
/* Check the length as a lame attempt to check for
* binary inconsistancy . */
if ( len ! = NO_LENGTH_CHECK ) {
/* Word-align len */
if ( len & 0x03 )
len = ( len + 3 ) & ~ 0x03 ;
if ( info - > len ! = len ) {
printk ( KERN_ERR " OMAP peripheral config: Length mismatch with tag %x (want %d, got %d) \n " ,
tag , len , info - > len ) ;
return NULL ;
}
}
if ( len_out ! = NULL )
* len_out = info - > len ;
return info - > data ;
}
# endif
/* Try to find the config from the board-specific structures
* in the kernel . */
for ( i = 0 ; i < omap_board_config_size ; i + + ) {
if ( omap_board_config [ i ] . tag = = tag ) {
2006-12-08 00:58:10 +03:00
if ( skip = = 0 ) {
kinfo = & omap_board_config [ i ] ;
break ;
} else {
skip - - ;
}
2005-07-10 22:58:15 +04:00
}
}
if ( kinfo = = NULL )
return NULL ;
return kinfo - > data ;
}
const void * __omap_get_config ( u16 tag , size_t len , int nr )
{
return get_config ( tag , len , nr , NULL ) ;
}
EXPORT_SYMBOL ( __omap_get_config ) ;
const void * omap_get_var_config ( u16 tag , size_t * len )
{
return get_config ( tag , NO_LENGTH_CHECK , 0 , len ) ;
}
EXPORT_SYMBOL ( omap_get_var_config ) ;
static int __init omap_add_serial_console ( void )
{
2005-11-10 17:26:50 +03:00
const struct omap_serial_console_config * con_info ;
const struct omap_uart_config * uart_info ;
static char speed [ 11 ] , * opt = NULL ;
int line , i , uart_idx ;
uart_info = omap_get_config ( OMAP_TAG_UART , struct omap_uart_config ) ;
con_info = omap_get_config ( OMAP_TAG_SERIAL_CONSOLE ,
struct omap_serial_console_config ) ;
if ( uart_info = = NULL | | con_info = = NULL )
return 0 ;
if ( con_info - > console_uart = = 0 )
return 0 ;
if ( con_info - > console_speed ) {
snprintf ( speed , sizeof ( speed ) , " %u " , con_info - > console_speed ) ;
opt = speed ;
}
2005-07-10 22:58:15 +04:00
2005-11-10 17:26:50 +03:00
uart_idx = con_info - > console_uart - 1 ;
if ( uart_idx > = OMAP_MAX_NR_PORTS ) {
printk ( KERN_INFO " Console: external UART#%d. "
" Not adding it as console this time. \n " ,
uart_idx + 1 ) ;
return 0 ;
}
if ( ! ( uart_info - > enabled_uarts & ( 1 < < uart_idx ) ) ) {
printk ( KERN_ERR " Console: Selected UART#%d is "
" not enabled for this platform \n " ,
uart_idx + 1 ) ;
return - 1 ;
}
line = 0 ;
for ( i = 0 ; i < uart_idx ; i + + ) {
if ( uart_info - > enabled_uarts & ( 1 < < i ) )
line + + ;
2005-07-10 22:58:15 +04:00
}
2005-11-10 17:26:50 +03:00
return add_preferred_console ( " ttyS " , line , opt ) ;
2005-07-10 22:58:15 +04:00
}
console_initcall ( omap_add_serial_console ) ;
2007-03-08 22:32:19 +03:00
/*
* 32 KHz clocksource . . . always available , on pretty most chips except
* OMAP 730 and 1510. Other timers could be used as clocksources , with
* higher resolution in free - running counter modes ( e . g . 12 MHz xtal ) ,
* but systems won ' t necessarily want to spend resources that way .
*/
# if defined(CONFIG_ARCH_OMAP16XX)
# define TIMER_32K_SYNCHRONIZED 0xfffbc410
# elif defined(CONFIG_ARCH_OMAP24XX)
# define TIMER_32K_SYNCHRONIZED 0x48004010
# endif
# ifdef TIMER_32K_SYNCHRONIZED
# include <linux/clocksource.h>
static cycle_t omap_32k_read ( void )
{
return omap_readl ( TIMER_32K_SYNCHRONIZED ) ;
}
static struct clocksource clocksource_32k = {
. name = " 32k_counter " ,
. rating = 250 ,
. read = omap_32k_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 10 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static int __init omap_init_clocksource_32k ( void )
{
static char err [ ] __initdata = KERN_ERR
" %s: can't register clocksource! \n " ;
if ( cpu_is_omap16xx ( ) | | cpu_is_omap24xx ( ) ) {
clocksource_32k . mult = clocksource_hz2mult ( 32768 ,
clocksource_32k . shift ) ;
if ( clocksource_register ( & clocksource_32k ) )
printk ( err , clocksource_32k . name ) ;
}
return 0 ;
}
arch_initcall ( omap_init_clocksource_32k ) ;
# endif /* TIMER_32K_SYNCHRONIZED */