2016-08-26 15:17:34 +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
2016-10-05 18:18:21 +01:00
* Free Software Foundation ; either version 2 of the License , or ( at your
2016-08-26 15:17:34 +01:00
* option ) any later version .
*/
2016-10-05 18:18:21 +01:00
# define pr_fmt(fmt) "sead3: " fmt
2016-08-26 15:17:34 +01:00
# include <linux/errno.h>
# include <linux/libfdt.h>
# include <linux/printk.h>
2016-08-26 15:17:35 +01:00
# include <asm/fw/fw.h>
2016-08-26 15:17:34 +01:00
# include <asm/io.h>
2016-10-05 18:18:21 +01:00
# include <asm/machine.h>
2017-06-02 12:29:51 -07:00
# include <asm/yamon-dt.h>
2016-08-26 15:17:34 +01:00
# define SEAD_CONFIG CKSEG1ADDR(0x1b100110)
# define SEAD_CONFIG_GIC_PRESENT BIT(1)
2016-10-05 18:18:21 +01:00
# define MIPS_REVISION CKSEG1ADDR(0x1fc00010)
# define MIPS_REVISION_MACHINE (0xf << 4)
# define MIPS_REVISION_MACHINE_SEAD3 (0x4 << 4)
2016-08-26 15:17:34 +01:00
2016-10-05 18:18:21 +01:00
static __init bool sead3_detect ( void )
{
uint32_t rev ;
rev = __raw_readl ( ( void * ) MIPS_REVISION ) ;
return ( rev & MIPS_REVISION_MACHINE ) = = MIPS_REVISION_MACHINE_SEAD3 ;
}
static __init int remove_gic ( void * fdt )
2016-08-26 15:17:34 +01:00
{
2016-08-26 15:17:38 +01:00
const unsigned int cpu_ehci_int = 2 ;
2016-08-26 15:17:35 +01:00
const unsigned int cpu_uart_int = 4 ;
2016-08-26 15:17:37 +01:00
const unsigned int cpu_eth_int = 6 ;
2016-08-26 15:17:38 +01:00
int gic_off , cpu_off , uart_off , eth_off , ehci_off , err ;
2016-08-26 15:17:34 +01:00
uint32_t cfg , cpu_phandle ;
/* leave the GIC node intact if a GIC is present */
cfg = __raw_readl ( ( uint32_t * ) SEAD_CONFIG ) ;
if ( cfg & SEAD_CONFIG_GIC_PRESENT )
return 0 ;
gic_off = fdt_node_offset_by_compatible ( fdt , - 1 , " mti,gic " ) ;
if ( gic_off < 0 ) {
pr_err ( " unable to find DT GIC node: %d \n " , gic_off ) ;
return gic_off ;
}
err = fdt_nop_node ( fdt , gic_off ) ;
if ( err ) {
pr_err ( " unable to nop GIC node \n " ) ;
return err ;
}
cpu_off = fdt_node_offset_by_compatible ( fdt , - 1 ,
" mti,cpu-interrupt-controller " ) ;
if ( cpu_off < 0 ) {
pr_err ( " unable to find CPU intc node: %d \n " , cpu_off ) ;
return cpu_off ;
}
cpu_phandle = fdt_get_phandle ( fdt , cpu_off ) ;
if ( ! cpu_phandle ) {
pr_err ( " unable to get CPU intc phandle \n " ) ;
return - EINVAL ;
}
err = fdt_setprop_u32 ( fdt , 0 , " interrupt-parent " , cpu_phandle ) ;
if ( err ) {
pr_err ( " unable to set root interrupt-parent: %d \n " , err ) ;
return err ;
}
2016-08-26 15:17:35 +01:00
uart_off = fdt_node_offset_by_compatible ( fdt , - 1 , " ns16550a " ) ;
while ( uart_off > = 0 ) {
err = fdt_setprop_u32 ( fdt , uart_off , " interrupts " ,
cpu_uart_int ) ;
if ( err ) {
pr_err ( " unable to set UART interrupts property: %d \n " ,
err ) ;
return err ;
}
uart_off = fdt_node_offset_by_compatible ( fdt , uart_off ,
" ns16550a " ) ;
}
if ( uart_off ! = - FDT_ERR_NOTFOUND ) {
pr_err ( " error searching for UART DT node: %d \n " , uart_off ) ;
return uart_off ;
}
2016-08-26 15:17:37 +01:00
eth_off = fdt_node_offset_by_compatible ( fdt , - 1 , " smsc,lan9115 " ) ;
if ( eth_off < 0 ) {
pr_err ( " unable to find ethernet DT node: %d \n " , eth_off ) ;
return eth_off ;
}
err = fdt_setprop_u32 ( fdt , eth_off , " interrupts " , cpu_eth_int ) ;
if ( err ) {
pr_err ( " unable to set ethernet interrupts property: %d \n " , err ) ;
return err ;
}
2016-10-05 18:18:21 +01:00
ehci_off = fdt_node_offset_by_compatible ( fdt , - 1 , " generic-ehci " ) ;
2016-08-26 15:17:38 +01:00
if ( ehci_off < 0 ) {
pr_err ( " unable to find EHCI DT node: %d \n " , ehci_off ) ;
return ehci_off ;
}
err = fdt_setprop_u32 ( fdt , ehci_off , " interrupts " , cpu_ehci_int ) ;
if ( err ) {
pr_err ( " unable to set EHCI interrupts property: %d \n " , err ) ;
return err ;
}
2016-08-26 15:17:35 +01:00
return 0 ;
}
2016-10-05 18:18:21 +01:00
static __init const void * sead3_fixup_fdt ( const void * fdt ,
const void * match_data )
2016-08-26 15:17:34 +01:00
{
2016-10-05 18:18:21 +01:00
static unsigned char fdt_buf [ 16 < < 10 ] __initdata ;
2016-08-26 15:17:34 +01:00
int err ;
if ( fdt_check_header ( fdt ) )
panic ( " Corrupt DT " ) ;
2016-10-05 18:18:21 +01:00
/* if this isn't SEAD3, something went wrong */
BUG_ON ( fdt_node_check_compatible ( fdt , 0 , " mti,sead-3 " ) ) ;
fw_init_cmdline ( ) ;
2016-08-26 15:17:34 +01:00
err = fdt_open_into ( fdt , fdt_buf , sizeof ( fdt_buf ) ) ;
if ( err )
panic ( " Unable to open FDT: %d " , err ) ;
2017-06-02 12:29:51 -07:00
err = yamon_dt_append_cmdline ( fdt_buf ) ;
2016-10-05 18:18:21 +01:00
if ( err )
panic ( " Unable to patch FDT: %d " , err ) ;
2017-06-02 12:29:51 -07:00
err = yamon_dt_append_memory ( fdt_buf ) ;
2016-08-26 15:17:45 +01:00
if ( err )
panic ( " Unable to patch FDT: %d " , err ) ;
2016-08-26 15:17:34 +01:00
err = remove_gic ( fdt_buf ) ;
if ( err )
panic ( " Unable to patch FDT: %d " , err ) ;
2017-06-02 12:29:51 -07:00
err = yamon_dt_serial_config ( fdt_buf ) ;
2016-08-26 15:17:35 +01:00
if ( err )
panic ( " Unable to patch FDT: %d " , err ) ;
2016-08-26 15:17:34 +01:00
err = fdt_pack ( fdt_buf ) ;
if ( err )
panic ( " Unable to pack FDT: %d \n " , err ) ;
return fdt_buf ;
}
2016-10-05 18:18:21 +01:00
static __init unsigned int sead3_measure_hpt_freq ( void )
{
void __iomem * status_reg = ( void __iomem * ) 0xbf000410 ;
unsigned int freq , orig , tick = 0 ;
unsigned long flags ;
local_irq_save ( flags ) ;
orig = readl ( status_reg ) & 0x2 ; /* get original sample */
/* wait for transition */
while ( ( readl ( status_reg ) & 0x2 ) = = orig )
;
orig = orig ^ 0x2 ; /* flip the bit */
write_c0_count ( 0 ) ;
/* wait 1 second (the sampling clock transitions every 10ms) */
while ( tick < 100 ) {
/* wait for transition */
while ( ( readl ( status_reg ) & 0x2 ) = = orig )
;
orig = orig ^ 0x2 ; /* flip the bit */
tick + + ;
}
freq = read_c0_count ( ) ;
local_irq_restore ( flags ) ;
return freq ;
}
extern char __dtb_sead3_begin [ ] ;
MIPS_MACHINE ( sead3 ) = {
. fdt = __dtb_sead3_begin ,
. detect = sead3_detect ,
. fixup_fdt = sead3_fixup_fdt ,
. measure_hpt_freq = sead3_measure_hpt_freq ,
} ;