2008-05-19 16:53:02 -07:00
/*
2005-04-16 15:20:36 -07:00
* linux / arch / sparc / kernel / setup . c
*
* Copyright ( C ) 1995 David S . Miller ( davem @ caip . rutgers . edu )
* Copyright ( C ) 2000 Anton Blanchard ( anton @ samba . org )
*/
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/initrd.h>
# include <asm/smp.h>
# include <linux/user.h>
2006-07-10 04:44:13 -07:00
# include <linux/screen_info.h>
2005-04-16 15:20:36 -07:00
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/seq_file.h>
# include <linux/syscalls.h>
# include <linux/kdev_t.h>
# include <linux/major.h>
# include <linux/string.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/console.h>
# include <linux/spinlock.h>
# include <linux/root_dev.h>
2006-06-20 00:28:33 -07:00
# include <linux/cpu.h>
2007-05-08 00:27:03 -07:00
# include <linux/kdebug.h>
2011-07-18 15:57:46 -04:00
# include <linux/export.h>
2012-05-25 21:20:08 +00:00
# include <linux/start_kernel.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/processor.h>
# include <asm/oplib.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/traps.h>
# include <asm/vaddrs.h>
# include <asm/mbus.h>
# include <asm/idprom.h>
# include <asm/cpudata.h>
# include <asm/setup.h>
2012-03-28 18:30:03 +01:00
# include <asm/cacheflush.h>
2012-05-25 21:20:09 +00:00
# include <asm/sections.h>
2005-04-16 15:20:36 -07:00
2008-12-07 00:02:08 -08:00
# include "kernel.h"
2005-04-16 15:20:36 -07:00
struct screen_info screen_info = {
0 , 0 , /* orig-x, orig-y */
0 , /* unused */
0 , /* orig-video-page */
0 , /* orig-video-mode */
128 , /* orig-video-cols */
0 , 0 , 0 , /* ega_ax, ega_bx, ega_cx */
54 , /* orig-video-lines */
0 , /* orig-video-isVGA */
16 /* orig-video-points */
} ;
/* Typing sync at the prom prompt calls the function pointed to by
* romvec - > pv_synchook which I set to the following function .
* This should sync all filesystems and return , for now it just
* prints out pretty messages and returns .
*/
extern unsigned long trapbase ;
/* Pretty sick eh? */
2008-06-05 11:40:58 -07:00
static void prom_sync_me ( void )
2005-04-16 15:20:36 -07:00
{
unsigned long prom_tbr , flags ;
/* XXX Badly broken. FIX! - Anton */
local_irq_save ( flags ) ;
__asm__ __volatile__ ( " rd %%tbr, %0 \n \t " : " =r " ( prom_tbr ) ) ;
__asm__ __volatile__ ( " wr %0, 0x0, %%tbr \n \t "
" nop \n \t "
" nop \n \t "
" nop \n \t " : : " r " ( & trapbase ) ) ;
prom_printf ( " PROM SYNC COMMAND... \n " ) ;
2011-05-24 17:11:16 -07:00
show_free_areas ( 0 ) ;
2011-11-10 15:56:46 -08:00
if ( ! is_idle_task ( current ) ) {
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ;
sys_sync ( ) ;
local_irq_disable ( ) ;
}
prom_printf ( " Returning to prom \n " ) ;
__asm__ __volatile__ ( " wr %0, 0x0, %%tbr \n \t "
" nop \n \t "
" nop \n \t "
" nop \n \t " : : " r " ( prom_tbr ) ) ;
local_irq_restore ( flags ) ;
}
2008-06-05 11:40:58 -07:00
static unsigned int boot_flags __initdata = 0 ;
2005-04-16 15:20:36 -07:00
# define BOOTME_DEBUG 0x1
/* Exported for mm/init.c:paging_init. */
unsigned long cmdline_memory_size __initdata = 0 ;
2011-04-21 04:20:23 +00:00
/* which CPU booted us (0xff = not set) */
unsigned char boot_cpu_id = 0xff ; /* 0xff will make it into DATA section... */
2005-04-16 15:20:36 -07:00
static void
prom_console_write ( struct console * con , const char * s , unsigned n )
{
prom_write ( s , n ) ;
}
2011-04-21 15:47:35 -07:00
static struct console prom_early_console = {
. name = " earlyprom " ,
2005-04-16 15:20:36 -07:00
. write = prom_console_write ,
2011-04-21 15:47:35 -07:00
. flags = CON_PRINTBUFFER | CON_BOOT ,
2005-04-16 15:20:36 -07:00
. index = - 1 ,
} ;
/*
* Process kernel command line switches that are specific to the
* SPARC or that require special low - level processing .
*/
static void __init process_switch ( char c )
{
switch ( c ) {
case ' d ' :
boot_flags | = BOOTME_DEBUG ;
break ;
case ' s ' :
break ;
case ' h ' :
prom_printf ( " boot_flags_init: Halt! \n " ) ;
prom_halt ( ) ;
break ;
case ' p ' :
2011-09-21 12:48:06 -07:00
prom_early_console . flags & = ~ CON_BOOT ;
2005-04-16 15:20:36 -07:00
break ;
default :
printk ( " Unknown boot switch (-%c) \n " , c ) ;
break ;
}
}
static void __init boot_flags_init ( char * commands )
{
while ( * commands ) {
/* Move to the start of the next "argument". */
while ( * commands & & * commands = = ' ' )
commands + + ;
/* Process any command switches, otherwise skip it. */
if ( * commands = = ' \0 ' )
break ;
if ( * commands = = ' - ' ) {
commands + + ;
while ( * commands & & * commands ! = ' ' )
process_switch ( * commands + + ) ;
continue ;
}
2007-07-20 16:59:26 -07:00
if ( ! strncmp ( commands , " mem= " , 4 ) ) {
2005-04-16 15:20:36 -07:00
/*
* " mem=XXX[kKmM] overrides the PROM-reported
* memory size .
*/
cmdline_memory_size = simple_strtoul ( commands + 4 ,
& commands , 0 ) ;
if ( * commands = = ' K ' | | * commands = = ' k ' ) {
cmdline_memory_size < < = 10 ;
commands + + ;
} else if ( * commands = = ' M ' | | * commands = = ' m ' ) {
cmdline_memory_size < < = 20 ;
commands + + ;
}
}
while ( * commands & & * commands ! = ' ' )
commands + + ;
}
}
extern unsigned short root_flags ;
extern unsigned short root_dev ;
extern unsigned short ram_flags ;
# define RAMDISK_IMAGE_START_MASK 0x07FF
# define RAMDISK_PROMPT_FLAG 0x8000
# define RAMDISK_LOAD_FLAG 0x4000
extern int root_mountflags ;
char reboot_command [ COMMAND_LINE_SIZE ] ;
2009-01-08 16:58:05 -08:00
2012-05-13 23:09:04 -07:00
struct cpuid_patch_entry {
unsigned int addr ;
unsigned int sun4d [ 3 ] ;
unsigned int leon [ 3 ] ;
} ;
extern struct cpuid_patch_entry __cpuid_patch , __cpuid_patch_end ;
static void __init per_cpu_patch ( void )
{
struct cpuid_patch_entry * p ;
if ( sparc_cpu_model = = sun4m ) {
/* Nothing to do, this is what the unpatched code
* targets .
*/
return ;
}
p = & __cpuid_patch ;
while ( p < & __cpuid_patch_end ) {
unsigned long addr = p - > addr ;
unsigned int * insns ;
switch ( sparc_cpu_model ) {
case sun4d :
insns = & p - > sun4d [ 0 ] ;
break ;
case sparc_leon :
insns = & p - > leon [ 0 ] ;
break ;
default :
prom_printf ( " Unknown cpu type, halting. \n " ) ;
prom_halt ( ) ;
}
* ( unsigned int * ) ( addr + 0 ) = insns [ 0 ] ;
2012-05-20 17:27:53 +02:00
flushi ( addr + 0 ) ;
2012-05-13 23:09:04 -07:00
* ( unsigned int * ) ( addr + 4 ) = insns [ 1 ] ;
2012-05-20 17:27:53 +02:00
flushi ( addr + 4 ) ;
2012-05-13 23:09:04 -07:00
* ( unsigned int * ) ( addr + 8 ) = insns [ 2 ] ;
2012-05-20 17:27:53 +02:00
flushi ( addr + 8 ) ;
2012-05-20 17:24:20 +02:00
p + + ;
2012-05-13 23:09:04 -07:00
}
}
2012-05-25 21:20:09 +00:00
struct leon_1insn_patch_entry {
unsigned int addr ;
unsigned int insn ;
} ;
2005-04-16 15:20:36 -07:00
enum sparc_cpu sparc_cpu_model ;
2009-01-08 16:58:05 -08:00
EXPORT_SYMBOL ( sparc_cpu_model ) ;
2005-04-16 15:20:36 -07:00
2012-05-25 21:20:09 +00:00
static __init void leon_patch ( void )
{
struct leon_1insn_patch_entry * start = ( void * ) __leon_1insn_patch ;
struct leon_1insn_patch_entry * end = ( void * ) __leon_1insn_patch_end ;
/* Default instruction is leon - no patching */
if ( sparc_cpu_model = = sparc_leon )
return ;
while ( start < end ) {
unsigned long addr = start - > addr ;
* ( unsigned int * ) ( addr ) = start - > insn ;
flushi ( addr ) ;
start + + ;
}
}
2005-04-16 15:20:36 -07:00
2012-05-25 21:20:09 +00:00
struct tt_entry * sparc_ttable ;
2005-04-16 15:20:36 -07:00
struct pt_regs fake_swapper_regs ;
2012-05-25 21:20:08 +00:00
/* Called from head_32.S - before we have setup anything
* in the kernel . Be very careful with what you do here .
*/
void __init sparc32_start_kernel ( struct linux_romvec * rp )
{
prom_init ( rp ) ;
2012-05-25 21:20:09 +00:00
/* Set sparc_cpu_model */
sparc_cpu_model = sun_unknown ;
if ( ! strcmp ( & cputypval [ 0 ] , " sun4m " ) )
sparc_cpu_model = sun4m ;
if ( ! strcmp ( & cputypval [ 0 ] , " sun4s " ) )
sparc_cpu_model = sun4m ; /* CP-1200 with PROM 2.30 -E */
if ( ! strcmp ( & cputypval [ 0 ] , " sun4d " ) )
sparc_cpu_model = sun4d ;
if ( ! strcmp ( & cputypval [ 0 ] , " sun4e " ) )
sparc_cpu_model = sun4e ;
if ( ! strcmp ( & cputypval [ 0 ] , " sun4u " ) )
sparc_cpu_model = sun4u ;
if ( ! strncmp ( & cputypval [ 0 ] , " leon " , 4 ) )
sparc_cpu_model = sparc_leon ;
leon_patch ( ) ;
2012-05-25 21:20:08 +00:00
start_kernel ( ) ;
}
2005-04-16 15:20:36 -07:00
void __init setup_arch ( char * * cmdline_p )
{
int i ;
unsigned long highest_paddr ;
2011-01-04 11:39:11 +00:00
sparc_ttable = ( struct tt_entry * ) & trapbase ;
2005-04-16 15:20:36 -07:00
/* Initialize PROM console and command line. */
* cmdline_p = prom_getbootargs ( ) ;
2007-02-12 00:54:21 -08:00
strcpy ( boot_command_line , * cmdline_p ) ;
2008-09-10 23:40:32 -07:00
parse_early_param ( ) ;
2005-04-16 15:20:36 -07:00
2011-04-21 15:47:35 -07:00
boot_flags_init ( * cmdline_p ) ;
register_console ( & prom_early_console ) ;
2005-04-16 15:20:36 -07:00
printk ( " ARCH: " ) ;
switch ( sparc_cpu_model ) {
case sun4m :
printk ( " SUN4M \n " ) ;
break ;
case sun4d :
printk ( " SUN4D \n " ) ;
break ;
case sun4e :
printk ( " SUN4E \n " ) ;
break ;
case sun4u :
printk ( " SUN4U \n " ) ;
break ;
2009-08-17 00:13:31 +00:00
case sparc_leon :
printk ( " LEON \n " ) ;
break ;
2005-04-16 15:20:36 -07:00
default :
printk ( " UNKNOWN! \n " ) ;
break ;
2011-06-03 14:45:23 +00:00
}
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_DUMMY_CONSOLE
conswitchp = & dummy_con ;
# endif
idprom_init ( ) ;
load_mmu ( ) ;
phys_base = 0xffffffffUL ;
highest_paddr = 0UL ;
for ( i = 0 ; sp_banks [ i ] . num_bytes ! = 0 ; i + + ) {
unsigned long top ;
if ( sp_banks [ i ] . base_addr < phys_base )
phys_base = sp_banks [ i ] . base_addr ;
top = sp_banks [ i ] . base_addr +
sp_banks [ i ] . num_bytes ;
if ( highest_paddr < top )
highest_paddr = top ;
}
pfn_base = phys_base > > PAGE_SHIFT ;
if ( ! root_flags )
root_mountflags & = ~ MS_RDONLY ;
ROOT_DEV = old_decode_dev ( root_dev ) ;
2006-06-22 14:47:14 -07:00
# ifdef CONFIG_BLK_DEV_RAM
2005-04-16 15:20:36 -07:00
rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK ;
rd_prompt = ( ( ram_flags & RAMDISK_PROMPT_FLAG ) ! = 0 ) ;
rd_doload = ( ( ram_flags & RAMDISK_LOAD_FLAG ) ! = 0 ) ;
# endif
prom_setsync ( prom_sync_me ) ;
if ( ( boot_flags & BOOTME_DEBUG ) & & ( linux_dbvec ! = 0 ) & &
( ( * ( short * ) linux_dbvec ) ! = - 1 ) ) {
printk ( " Booted under KADB. Syncing trap table. \n " ) ;
( * ( linux_dbvec - > teach_debugger ) ) ( ) ;
}
init_task . thread . kregs = & fake_swapper_regs ;
2012-05-20 17:27:53 +02:00
/* Run-time patch instructions to match the cpu model */
2012-05-13 23:09:04 -07:00
per_cpu_patch ( ) ;
2012-05-20 17:27:53 +02:00
paging_init ( ) ;
2006-08-25 00:46:02 -07:00
smp_setup_cpu_possible_map ( ) ;
2005-04-16 15:20:36 -07:00
}
extern int stop_a_enabled ;
void sun_do_break ( void )
{
if ( ! stop_a_enabled )
return ;
printk ( " \n " ) ;
flush_user_windows ( ) ;
prom_cmdline ( ) ;
}
2009-01-08 16:58:05 -08:00
EXPORT_SYMBOL ( sun_do_break ) ;
2005-04-16 15:20:36 -07:00
int stop_a_enabled = 1 ;
2006-06-20 00:28:33 -07:00
static int __init topology_init ( void )
{
int i , ncpus , err ;
/* Count the number of physically present processors in
* the machine , even on uniprocessor , so that / proc / cpuinfo
* output is consistent with 2.4 . x
*/
ncpus = 0 ;
while ( ! cpu_find_by_instance ( ncpus , NULL , NULL ) )
ncpus + + ;
ncpus_probed = ncpus ;
err = 0 ;
for_each_online_cpu ( i ) {
struct cpu * p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p )
err = - ENOMEM ;
else
2006-06-29 02:24:27 -07:00
register_cpu ( p , i ) ;
2006-06-20 00:28:33 -07:00
}
return err ;
}
subsys_initcall ( topology_init ) ;