2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2000 , 2001 , 2002 , 2003 Broadcom Corporation
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/linkage.h>
# include <linux/mm.h>
2018-10-30 15:09:49 -07:00
# include <linux/memblock.h>
2006-01-18 17:37:07 +00:00
# include <linux/pm.h>
2005-04-16 15:20:36 -07:00
# include <linux/smp.h>
# include <asm/bootinfo.h>
# include <asm/reboot.h>
2018-07-13 17:51:56 +02:00
# include <asm/setup.h>
2005-04-16 15:20:36 -07:00
# include <asm/sibyte/board.h>
2007-11-19 12:23:51 +00:00
# include <asm/smp-ops.h>
2005-04-16 15:20:36 -07:00
2007-09-05 08:58:26 +02:00
# include <asm/fw/cfe/cfe_api.h>
# include <asm/fw/cfe/cfe_error.h>
2005-04-16 15:20:36 -07:00
/* Max ram addressable in 32-bit segments */
2005-09-03 15:56:16 -07:00
# ifdef CONFIG_64BIT
2005-04-16 15:20:36 -07:00
# define MAX_RAM_SIZE (~0ULL)
# else
# ifdef CONFIG_HIGHMEM
2014-11-22 00:16:48 +01:00
# ifdef CONFIG_PHYS_ADDR_T_64BIT
2005-04-16 15:20:36 -07:00
# define MAX_RAM_SIZE (~0ULL)
# else
# define MAX_RAM_SIZE (0xffffffffULL)
# endif
# else
# define MAX_RAM_SIZE (0x1fffffffULL)
# endif
# endif
# define SIBYTE_MAX_MEM_REGIONS 8
2014-11-22 00:22:09 +01:00
phys_addr_t board_mem_region_addrs [ SIBYTE_MAX_MEM_REGIONS ] ;
phys_addr_t board_mem_region_sizes [ SIBYTE_MAX_MEM_REGIONS ] ;
2005-04-16 15:20:36 -07:00
unsigned int board_mem_region_count ;
int cfe_cons_handle ;
# ifdef CONFIG_BLK_DEV_INITRD
extern unsigned long initrd_start , initrd_end ;
# endif
2007-05-25 14:32:28 -04:00
static void __noreturn cfe_linux_exit ( void * arg )
2005-04-16 15:20:36 -07:00
{
int warm = * ( int * ) arg ;
if ( smp_processor_id ( ) ) {
static int reboot_smp ;
/* Don't repeat the process from another CPU */
if ( ! reboot_smp ) {
/* Get CPU 0 to do the cfe_exit */
reboot_smp = 1 ;
2008-06-06 11:18:06 +02:00
smp_call_function ( cfe_linux_exit , arg , 0 ) ;
2005-04-16 15:20:36 -07:00
}
} else {
printk ( " Passing control back to CFE... \n " ) ;
cfe_exit ( warm , 0 ) ;
printk ( " cfe_exit returned?? \n " ) ;
}
while ( 1 ) ;
}
2007-05-25 14:32:28 -04:00
static void __noreturn cfe_linux_restart ( char * command )
2005-04-16 15:20:36 -07:00
{
static const int zero ;
cfe_linux_exit ( ( void * ) & zero ) ;
}
2007-05-25 14:32:28 -04:00
static void __noreturn cfe_linux_halt ( void )
2005-04-16 15:20:36 -07:00
{
static const int one = 1 ;
cfe_linux_exit ( ( void * ) & one ) ;
}
static __init void prom_meminit ( void )
{
2014-11-22 00:16:48 +01:00
u64 addr , size , type ; /* regardless of PHYS_ADDR_T_64BIT */
2005-04-16 15:20:36 -07:00
int mem_flags = 0 ;
unsigned int idx ;
int rd_flag ;
# ifdef CONFIG_BLK_DEV_INITRD
unsigned long initrd_pstart ;
unsigned long initrd_pend ;
initrd_pstart = CPHYSADDR ( initrd_start ) ;
initrd_pend = CPHYSADDR ( initrd_end ) ;
if ( initrd_start & &
( ( initrd_pstart > MAX_RAM_SIZE )
| | ( initrd_pend > MAX_RAM_SIZE ) ) ) {
panic ( " initrd out of addressable memory " ) ;
}
# endif /* INITRD */
for ( idx = 0 ; cfe_enummem ( idx , mem_flags , & addr , & size , & type ) ! = CFE_ERR_NOMORE ;
idx + + ) {
rd_flag = 0 ;
if ( type = = CFE_MI_AVAILABLE ) {
/*
* See if this block contains ( any portion of ) the
* ramdisk
*/
# ifdef CONFIG_BLK_DEV_INITRD
if ( initrd_start ) {
if ( ( initrd_pstart > addr ) & &
( initrd_pstart < ( addr + size ) ) ) {
2020-10-09 14:14:46 +02:00
memblock_add ( addr ,
initrd_pstart - addr ) ;
2005-04-16 15:20:36 -07:00
rd_flag = 1 ;
}
if ( ( initrd_pend > addr ) & &
( initrd_pend < ( addr + size ) ) ) {
2020-10-09 14:14:46 +02:00
memblock_add ( initrd_pend ,
( addr + size ) - initrd_pend ) ;
2005-04-16 15:20:36 -07:00
rd_flag = 1 ;
}
}
# endif
if ( ! rd_flag ) {
if ( addr > MAX_RAM_SIZE )
continue ;
if ( addr + size > MAX_RAM_SIZE )
size = MAX_RAM_SIZE - ( addr + size ) + 1 ;
/*
* memcpy / __copy_user prefetch , which
* will cause a bus error for
* KSEG / KUSEG addrs not backed by RAM .
* Hence , reserve some padding for the
* prefetch distance .
*/
if ( size > 512 )
size - = 512 ;
2020-10-09 14:14:46 +02:00
memblock_add ( addr , size ) ;
2005-04-16 15:20:36 -07:00
}
board_mem_region_addrs [ board_mem_region_count ] = addr ;
board_mem_region_sizes [ board_mem_region_count ] = size ;
board_mem_region_count + + ;
if ( board_mem_region_count = =
SIBYTE_MAX_MEM_REGIONS ) {
/*
* Too many regions . Need to configure more
*/
while ( 1 ) ;
}
}
}
# ifdef CONFIG_BLK_DEV_INITRD
if ( initrd_start ) {
2020-10-09 14:14:46 +02:00
memblock_add ( initrd_pstart , initrd_pend - initrd_pstart ) ;
memblock_reserve ( initrd_pstart , initrd_pend - initrd_pstart ) ;
2005-04-16 15:20:36 -07:00
}
# endif
}
# ifdef CONFIG_BLK_DEV_INITRD
static int __init initrd_setup ( char * str )
{
char rdarg [ 64 ] ;
int idx ;
char * tmp , * endptr ;
unsigned long initrd_size ;
/* Make a copy of the initrd argument so we can smash it up here */
for ( idx = 0 ; idx < sizeof ( rdarg ) - 1 ; idx + + ) {
if ( ! str [ idx ] | | ( str [ idx ] = = ' ' ) ) break ;
rdarg [ idx ] = str [ idx ] ;
}
rdarg [ idx ] = 0 ;
str = rdarg ;
/*
* Initrd location comes in the form " <hex size of ramdisk in bytes>@<location in memory> "
2013-01-22 12:59:30 +01:00
* e . g . initrd = 3 abfd @ 80010000. This is set up by the loader .
2005-04-16 15:20:36 -07:00
*/
for ( tmp = str ; * tmp ! = ' @ ' ; tmp + + ) {
if ( ! * tmp ) {
goto fail ;
}
}
* tmp = 0 ;
tmp + + ;
if ( ! * tmp ) {
goto fail ;
}
initrd_size = simple_strtoul ( str , & endptr , 16 ) ;
if ( * endptr ) {
* ( tmp - 1 ) = ' @ ' ;
goto fail ;
}
* ( tmp - 1 ) = ' @ ' ;
initrd_start = simple_strtoul ( tmp , & endptr , 16 ) ;
if ( * endptr ) {
goto fail ;
}
initrd_end = initrd_start + initrd_size ;
2007-03-01 11:56:43 +00:00
printk ( " Found initrd of %lx@%lx \n " , initrd_size , initrd_start ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
fail :
2007-03-01 11:56:43 +00:00
printk ( " Bad initrd argument. Disabling initrd \n " ) ;
2005-04-16 15:20:36 -07:00
initrd_start = 0 ;
initrd_end = 0 ;
return 1 ;
}
# endif
2017-07-19 09:21:03 +01:00
extern const struct plat_smp_ops sb_smp_ops ;
extern const struct plat_smp_ops bcm1480_smp_ops ;
2007-11-19 12:23:51 +00:00
2005-04-16 15:20:36 -07:00
/*
* prom_init is called just after the cpu type is determined , from setup_arch ( )
*/
void __init prom_init ( void )
{
uint64_t cfe_ept , cfe_handle ;
unsigned int cfe_eptseal ;
int argc = fw_arg0 ;
char * * envp = ( char * * ) fw_arg2 ;
int * prom_vec = ( int * ) fw_arg3 ;
_machine_restart = cfe_linux_restart ;
2013-01-22 12:59:30 +01:00
_machine_halt = cfe_linux_halt ;
2006-01-18 17:37:07 +00:00
pm_power_off = cfe_linux_halt ;
2005-04-16 15:20:36 -07:00
/*
* Check if a loader was used ; if NOT , the 4 arguments are
* what CFE gives us ( handle , 0 , EPT and EPTSEAL )
*/
if ( argc < 0 ) {
cfe_handle = ( uint64_t ) ( long ) argc ;
cfe_ept = ( long ) envp ;
cfe_eptseal = ( uint32_t ) ( unsigned long ) prom_vec ;
} else {
if ( ( int32_t ) ( long ) prom_vec < 0 ) {
/*
* Old loader ; all it gives us is the handle ,
* so use the " known " entrypoint and assume
* the seal .
*/
cfe_handle = ( uint64_t ) ( long ) prom_vec ;
cfe_ept = ( uint64_t ) ( ( int32_t ) 0x9fc00500 ) ;
cfe_eptseal = CFE_EPTSEAL ;
} else {
/*
* Newer loaders bundle the handle / ept / eptseal
* Note : prom_vec is in the loader ' s useg
* which is still alive in the TLB .
*/
cfe_handle = ( uint64_t ) ( ( int32_t * ) prom_vec ) [ 0 ] ;
cfe_ept = ( uint64_t ) ( ( int32_t * ) prom_vec ) [ 2 ] ;
cfe_eptseal = ( unsigned int ) ( ( uint32_t * ) prom_vec ) [ 3 ] ;
}
}
if ( cfe_eptseal ! = CFE_EPTSEAL ) {
/* too early for panic to do any good */
2007-03-01 11:56:43 +00:00
printk ( " CFE's entrypoint seal doesn't match. Spinning. " ) ;
2005-04-16 15:20:36 -07:00
while ( 1 ) ;
}
cfe_init ( cfe_handle , cfe_ept ) ;
2005-09-03 15:56:17 -07:00
/*
2005-04-16 15:20:36 -07:00
* Get the handle for ( at least ) prom_putchar , possibly for
* boot console
*/
cfe_cons_handle = cfe_getstdhandle ( CFE_STDHANDLE_CONSOLE ) ;
2009-10-13 23:43:24 +03:00
if ( cfe_getenv ( " LINUX_CMDLINE " , arcs_cmdline , COMMAND_LINE_SIZE ) < 0 ) {
2009-06-02 19:05:28 +01:00
if ( argc > = 0 ) {
2005-04-16 15:20:36 -07:00
/* The loader should have set the command line */
/* too early for panic to do any good */
2007-03-01 11:56:43 +00:00
printk ( " LINUX_CMDLINE not defined in cfe. " ) ;
2005-04-16 15:20:36 -07:00
while ( 1 ) ;
}
}
# ifdef CONFIG_BLK_DEV_INITRD
{
char * ptr ;
2013-01-22 12:59:30 +01:00
/* Need to find out early whether we've got an initrd. So scan
2005-04-16 15:20:36 -07:00
the list looking now */
for ( ptr = arcs_cmdline ; * ptr ; ptr + + ) {
while ( * ptr = = ' ' ) {
ptr + + ;
}
if ( ! strncmp ( ptr , " initrd= " , 7 ) ) {
initrd_setup ( ptr + 7 ) ;
break ;
} else {
while ( * ptr & & ( * ptr ! = ' ' ) ) {
ptr + + ;
}
}
}
}
# endif /* CONFIG_BLK_DEV_INITRD */
/* Not sure this is needed, but it's the safe way. */
2009-10-13 23:43:24 +03:00
arcs_cmdline [ COMMAND_LINE_SIZE - 1 ] = 0 ;
2005-04-16 15:20:36 -07:00
prom_meminit ( ) ;
2007-11-19 12:23:51 +00:00
# if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
register_smp_ops ( & sb_smp_ops ) ;
# endif
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
register_smp_ops ( & bcm1480_smp_ops ) ;
# endif
2005-04-16 15:20:36 -07:00
}
void prom_putchar ( char c )
{
int ret ;
while ( ( ret = cfe_write ( cfe_cons_handle , & c , 1 ) ) = = 0 )
;
}