2016-10-05 18:18:20 +01:00
/*
* Copyright ( C ) 2016 Imagination Technologies
* Author : Paul Burton < paul . burton @ imgtec . com >
*
* 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 .
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/clocksource.h>
# include <linux/init.h>
# include <linux/irqchip.h>
# include <linux/of_fdt.h>
# include <linux/of_platform.h>
2017-08-23 11:17:44 -07:00
# include <asm/bootinfo.h>
2016-10-05 18:18:20 +01:00
# include <asm/fw/fw.h>
# include <asm/irq_cpu.h>
# include <asm/machine.h>
# include <asm/mips-cpc.h>
# include <asm/prom.h>
# include <asm/smp-ops.h>
# include <asm/time.h>
static __initdata const void * fdt ;
static __initdata const struct mips_machine * mach ;
static __initdata const void * mach_match_data ;
void __init prom_init ( void )
2016-10-17 17:25:24 +01:00
{
plat_get_fdt ( ) ;
BUG_ON ( ! fdt ) ;
}
void __init * plat_get_fdt ( void )
2016-10-05 18:18:20 +01:00
{
const struct mips_machine * check_mach ;
const struct of_device_id * match ;
2016-10-17 17:25:24 +01:00
if ( fdt )
/* Already set up */
return ( void * ) fdt ;
2016-10-05 18:18:20 +01:00
if ( ( fw_arg0 = = - 2 ) & & ! fdt_check_header ( ( void * ) fw_arg1 ) ) {
/*
* We booted using the UHI boot protocol , so we have been
* provided with the appropriate device tree for the board .
* Make use of it & search for any machine struct based upon
* the root compatible string .
*/
fdt = ( void * ) fw_arg1 ;
for_each_mips_machine ( check_mach ) {
match = mips_machine_is_compatible ( check_mach , fdt ) ;
if ( match ) {
mach = check_mach ;
mach_match_data = match - > data ;
break ;
}
}
} else if ( IS_ENABLED ( CONFIG_LEGACY_BOARDS ) ) {
/*
* We weren ' t booted using the UHI boot protocol , but do
* support some number of boards with legacy boot protocols .
* Attempt to find the right one .
*/
for_each_mips_machine ( check_mach ) {
if ( ! check_mach - > detect )
continue ;
if ( ! check_mach - > detect ( ) )
continue ;
mach = check_mach ;
}
/*
* If we don ' t recognise the machine then we can ' t continue , so
* die here .
*/
BUG_ON ( ! mach ) ;
/* Retrieve the machine's FDT */
fdt = mach - > fdt ;
}
return ( void * ) fdt ;
}
2017-08-23 11:17:44 -07:00
# ifdef CONFIG_RELOCATABLE
2016-11-23 14:43:47 +01:00
void __init plat_fdt_relocated ( void * new_location )
{
/*
* reset fdt as the cached value would point to the location
* before relocations happened and update the location argument
* if it was passed using UHI
*/
fdt = NULL ;
if ( fw_arg0 = = - 2 )
fw_arg1 = ( unsigned long ) new_location ;
}
2017-08-23 11:17:44 -07:00
# endif /* CONFIG_RELOCATABLE */
2016-10-05 18:18:20 +01:00
void __init plat_mem_setup ( void )
{
if ( mach & & mach - > fixup_fdt )
fdt = mach - > fixup_fdt ( fdt , mach_match_data ) ;
strlcpy ( arcs_cmdline , boot_command_line , COMMAND_LINE_SIZE ) ;
__dt_setup_arch ( ( void * ) fdt ) ;
}
void __init device_tree_init ( void )
{
int err ;
unflatten_and_copy_device_tree ( ) ;
mips_cpc_probe ( ) ;
err = register_cps_smp_ops ( ) ;
if ( err )
err = register_up_smp_ops ( ) ;
}
2017-06-02 12:29:54 -07:00
int __init apply_mips_fdt_fixups ( void * fdt_out , size_t fdt_out_size ,
const void * fdt_in ,
const struct mips_fdt_fixup * fixups )
{
int err ;
err = fdt_open_into ( fdt_in , fdt_out , fdt_out_size ) ;
if ( err ) {
pr_err ( " Failed to open FDT \n " ) ;
return err ;
}
for ( ; fixups - > apply ; fixups + + ) {
err = fixups - > apply ( fdt_out ) ;
if ( err ) {
pr_err ( " Failed to apply FDT fixup \" %s \" \n " ,
fixups - > description ) ;
return err ;
}
}
err = fdt_pack ( fdt_out ) ;
if ( err )
pr_err ( " Failed to pack FDT \n " ) ;
return err ;
}
2016-10-05 18:18:20 +01:00
void __init plat_time_init ( void )
{
struct device_node * np ;
struct clk * clk ;
of_clk_init ( NULL ) ;
if ( ! cpu_has_counter ) {
mips_hpt_frequency = 0 ;
} else if ( mach & & mach - > measure_hpt_freq ) {
mips_hpt_frequency = mach - > measure_hpt_freq ( ) ;
} else {
np = of_get_cpu_node ( 0 , NULL ) ;
if ( ! np ) {
pr_err ( " Failed to get CPU node \n " ) ;
return ;
}
clk = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to get CPU clock: %ld \n " , PTR_ERR ( clk ) ) ;
return ;
}
mips_hpt_frequency = clk_get_rate ( clk ) ;
clk_put ( clk ) ;
switch ( boot_cpu_type ( ) ) {
case CPU_20KC :
case CPU_25KF :
/* The counter runs at the CPU clock rate */
break ;
default :
/* The counter runs at half the CPU clock rate */
mips_hpt_frequency / = 2 ;
break ;
}
}
2017-05-26 17:40:46 +02:00
timer_probe ( ) ;
2016-10-05 18:18:20 +01:00
}
void __init arch_init_irq ( void )
{
struct device_node * intc_node ;
intc_node = of_find_compatible_node ( NULL , NULL ,
" mti,cpu-interrupt-controller " ) ;
if ( ! cpu_has_veic & & ! intc_node )
mips_cpu_irq_init ( ) ;
irqchip_init ( ) ;
}
static int __init publish_devices ( void )
{
if ( ! of_have_populated_dt ( ) )
panic ( " Device-tree not present " ) ;
if ( of_platform_populate ( NULL , of_default_bus_match_table , NULL , NULL ) )
panic ( " Failed to populate DT " ) ;
return 0 ;
}
arch_initcall ( publish_devices ) ;
void __init prom_free_prom_memory ( void )
{
}