2014-04-19 02:19:55 +04:00
/*
* Copyright ( C ) 2014 Linaro Ltd .
* Author : Rob Herring < robh @ kernel . org >
*
* Based on 8250 earlycon :
* ( c ) Copyright 2004 Hewlett - Packard Development Company , L . P .
* Bjorn Helgaas < bjorn . helgaas @ hp . com >
*
* 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 .
*/
2015-03-09 23:27:21 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2014-04-19 02:19:55 +04:00
# include <linux/console.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/serial_core.h>
2014-03-27 17:06:16 +04:00
# include <linux/sizes.h>
2016-01-17 02:23:44 +03:00
# include <linux/of.h>
2016-01-17 02:23:43 +03:00
# include <linux/of_fdt.h>
2016-09-27 23:54:13 +03:00
# include <linux/acpi.h>
2014-04-19 02:19:55 +04:00
# ifdef CONFIG_FIX_EARLYCON_MEM
# include <asm/fixmap.h>
# endif
# include <asm/serial.h>
static struct console early_con = {
2016-01-17 02:23:40 +03:00
. name = " uart " , /* fixed up at earlycon registration */
2014-04-19 02:19:55 +04:00
. flags = CON_PRINTBUFFER | CON_BOOT ,
2016-01-17 02:23:40 +03:00
. index = 0 ,
2014-04-19 02:19:55 +04:00
} ;
static struct earlycon_device early_console_dev = {
. con = & early_con ,
} ;
2016-09-02 14:20:21 +03:00
static void __iomem * __init earlycon_map ( resource_size_t paddr , size_t size )
2014-04-19 02:19:55 +04:00
{
void __iomem * base ;
# ifdef CONFIG_FIX_EARLYCON_MEM
set_fixmap_io ( FIX_EARLYCON_MEM_BASE , paddr & PAGE_MASK ) ;
base = ( void __iomem * ) __fix_to_virt ( FIX_EARLYCON_MEM_BASE ) ;
base + = paddr & ~ PAGE_MASK ;
# else
base = ioremap ( paddr , size ) ;
# endif
if ( ! base )
2016-09-02 14:20:21 +03:00
pr_err ( " %s: Couldn't map %pa \n " , __func__ , & paddr ) ;
2014-04-19 02:19:55 +04:00
return base ;
}
2016-01-17 02:23:40 +03:00
static void __init earlycon_init ( struct earlycon_device * device ,
const char * name )
{
struct console * earlycon = device - > con ;
2016-01-17 02:23:45 +03:00
struct uart_port * port = & device - > port ;
2016-01-17 02:23:40 +03:00
const char * s ;
size_t len ;
/* scan backwards from end of string for first non-numeral */
for ( s = name + strlen ( name ) ;
s > name & & s [ - 1 ] > = ' 0 ' & & s [ - 1 ] < = ' 9 ' ;
s - - )
;
if ( * s )
earlycon - > index = simple_strtoul ( s , NULL , 10 ) ;
len = s - name ;
strlcpy ( earlycon - > name , name , min ( len + 1 , sizeof ( earlycon - > name ) ) ) ;
earlycon - > data = & early_console_dev ;
2016-01-17 02:23:45 +03:00
if ( port - > iotype = = UPIO_MEM | | port - > iotype = = UPIO_MEM16 | |
port - > iotype = = UPIO_MEM32 | | port - > iotype = = UPIO_MEM32BE )
2016-01-17 02:23:46 +03:00
pr_info ( " %s%d at MMIO%s %pa (options '%s') \n " ,
earlycon - > name , earlycon - > index ,
2016-01-17 02:23:45 +03:00
( port - > iotype = = UPIO_MEM ) ? " " :
( port - > iotype = = UPIO_MEM16 ) ? " 16 " :
( port - > iotype = = UPIO_MEM32 ) ? " 32 " : " 32be " ,
2016-01-17 02:23:46 +03:00
& port - > mapbase , device - > options ) ;
2016-01-17 02:23:45 +03:00
else
2016-01-17 02:23:46 +03:00
pr_info ( " %s%d at I/O port 0x%lx (options '%s') \n " ,
earlycon - > name , earlycon - > index ,
port - > iobase , device - > options ) ;
2016-01-17 02:23:40 +03:00
}
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
static int __init parse_options ( struct earlycon_device * device , char * options )
2014-04-19 02:19:55 +04:00
{
struct uart_port * port = & device - > port ;
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
int length ;
2016-09-02 14:20:21 +03:00
resource_size_t addr ;
2014-04-19 02:19:55 +04:00
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
if ( uart_parse_earlycon ( options , & port - > iotype , & addr , & options ) )
return - EINVAL ;
2014-04-19 02:19:55 +04:00
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
switch ( port - > iotype ) {
2015-10-28 06:46:05 +03:00
case UPIO_MEM :
port - > mapbase = addr ;
break ;
case UPIO_MEM16 :
port - > regshift = 1 ;
port - > mapbase = addr ;
break ;
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
case UPIO_MEM32 :
2015-05-25 06:54:28 +03:00
case UPIO_MEM32BE :
2015-10-28 06:46:05 +03:00
port - > regshift = 2 ;
2014-04-19 02:19:55 +04:00
port - > mapbase = addr ;
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
break ;
case UPIO_PORT :
2014-04-19 02:19:55 +04:00
port - > iobase = addr ;
serial: earlycon: Refactor parse_options into serial core
Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-03-01 19:05:46 +03:00
break ;
default :
2014-04-19 02:19:55 +04:00
return - EINVAL ;
}
if ( options ) {
2014-05-01 04:48:29 +04:00
device - > baud = simple_strtoul ( options , NULL , 0 ) ;
2014-04-19 02:19:55 +04:00
length = min ( strcspn ( options , " " ) + 1 ,
( size_t ) ( sizeof ( device - > options ) ) ) ;
strlcpy ( device - > options , options , length ) ;
}
return 0 ;
}
2015-03-09 23:27:21 +03:00
static int __init register_earlycon ( char * buf , const struct earlycon_id * match )
2014-04-19 02:19:55 +04:00
{
int err ;
struct uart_port * port = & early_console_dev . port ;
/* On parsing error, pass the options buf to the setup function */
2015-03-09 23:27:19 +03:00
if ( buf & & ! parse_options ( & early_console_dev , buf ) )
2014-04-19 02:19:55 +04:00
buf = NULL ;
serial: earlycon: Add missing spinlock initialization
If an earlycon console driver needs to acquire the uart_port.lock
spinlock for serial console output, and CONFIG_DEBUG_SPINLOCK=y:
BUG: spinlock bad magic on CPU#0, swapper/0
lock: sci_ports+0x0/0x3480, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 0 Comm: swapper Not tainted 4.4.0-rc2-koelsch-g62ea5edf143bb1d0-dirty #2083
Hardware name: Generic R8A7791 (Flattened Device Tree)
[<c00173a0>] (unwind_backtrace) from [<c0013094>] (show_stack+0x10/0x14)
[<c0013094>] (show_stack) from [<c01f2338>] (dump_stack+0x70/0x8c)
[<c01f2338>] (dump_stack) from [<c00702d8>] (do_raw_spin_lock+0x20/0x190)
[<c00702d8>] (do_raw_spin_lock) from [<c0267590>] (serial_console_write+0x4c/0x130)
[<c0267590>] (serial_console_write) from [<c00734c4>] (call_console_drivers.constprop.13+0xc8/0xec)
[<c00734c4>] (call_console_drivers.constprop.13) from [<c0074ef0>] (console_unlock+0x354/0x440)
[<c0074ef0>] (console_unlock) from [<c0075bb4>] (register_console+0x2a0/0x394)
[<c0075bb4>] (register_console) from [<c06cb750>] (of_setup_earlycon+0x90/0xa4)
[<c06cb750>] (of_setup_earlycon) from [<c06cfb60>] (setup_of_earlycon+0x118/0x13c)
[<c06cfb60>] (setup_of_earlycon) from [<c06b34ac>] (do_early_param+0x64/0xb4)
[<c06b34ac>] (do_early_param) from [<c00472c0>] (parse_args+0x254/0x350)
[<c00472c0>] (parse_args) from [<c06b3860>] (parse_early_options+0x2c/0x3c)
[<c06b3860>] (parse_early_options) from [<c06b389c>] (parse_early_param+0x2c/0x40)
[<c06b389c>] (parse_early_param) from [<c06b5b08>] (setup_arch+0x520/0xaf0)
[<c06b5b08>] (setup_arch) from [<c06b3948>] (start_kernel+0x94/0x370)
[<c06b3948>] (start_kernel) from [<40008090>] (0x40008090)
Initialize the spinlock in of_setup_earlycon() and register_earlycon(),
to fix this for both DT-based and legacy earlycon. If the driver would
reinitialize the spinlock again, this is harmless, as it's allowed to
reinitialize an unlocked spinlock.
Alternatives are:
- Drivers having an early_serial_console_write() that only performs
the core functionality of serial_console_write(), without acquiring
the lock (which may be unsafe, depending on the hardware),
- Drivers initializing the spinlock in their private earlycon setup
functions.
As uart_port is owned by generic serial_core, and uart_port.lock is
initialized by uart_add_one_port() for the normal case, this can better
be handled in the earlycon core.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Reported-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-27 13:13:48 +03:00
spin_lock_init ( & port - > lock ) ;
2015-03-09 23:27:15 +03:00
port - > uartclk = BASE_BAUD * 16 ;
2014-04-19 02:19:55 +04:00
if ( port - > mapbase )
port - > membase = earlycon_map ( port - > mapbase , 64 ) ;
2016-01-17 02:23:40 +03:00
earlycon_init ( & early_console_dev , match - > name ) ;
2015-03-09 23:27:21 +03:00
err = match - > setup ( & early_console_dev , buf ) ;
2014-04-19 02:19:55 +04:00
if ( err < 0 )
return err ;
if ( ! early_console_dev . con - > write )
return - ENODEV ;
register_console ( early_console_dev . con ) ;
return 0 ;
}
2014-03-27 17:06:16 +04:00
2015-03-09 23:27:21 +03:00
/**
* setup_earlycon - match and register earlycon console
* @ buf : earlycon param string
*
* Registers the earlycon console matching the earlycon specified
* in the param string @ buf . Acceptable param strings are of the form
2015-05-25 06:54:28 +03:00
* < name > , io | mmio | mmio32 | mmio32be , < addr > , < options >
2015-03-09 23:27:21 +03:00
* < name > , 0 x < addr > , < options >
* < name > , < options >
* < name >
*
* Only for the third form does the earlycon setup ( ) method receive the
* < options > string in the ' options ' parameter ; all other forms set
* the parameter to NULL .
*
* Returns 0 if an attempt to register the earlycon was made ,
* otherwise negative error code
*/
int __init setup_earlycon ( char * buf )
2015-03-09 23:27:20 +03:00
{
2015-03-09 23:27:21 +03:00
const struct earlycon_id * match ;
2015-03-09 23:27:20 +03:00
2015-03-09 23:27:21 +03:00
if ( ! buf | | ! buf [ 0 ] )
return - EINVAL ;
2015-03-09 23:27:20 +03:00
2015-03-09 23:27:21 +03:00
if ( early_con . flags & CON_ENABLED )
return - EALREADY ;
2015-03-09 23:27:20 +03:00
2016-01-17 02:23:39 +03:00
for ( match = __earlycon_table ; match < __earlycon_table_end ; match + + ) {
2015-03-09 23:27:21 +03:00
size_t len = strlen ( match - > name ) ;
2015-03-09 23:27:20 +03:00
2015-03-09 23:27:21 +03:00
if ( strncmp ( buf , match - > name , len ) )
continue ;
if ( buf [ len ] ) {
if ( buf [ len ] ! = ' , ' )
continue ;
buf + = len + 1 ;
} else
buf = NULL ;
return register_earlycon ( buf , match ) ;
}
return - ENOENT ;
}
2016-09-27 23:54:13 +03:00
/*
* When CONFIG_ACPI_SPCR_TABLE is defined , " earlycon " without parameters in
* command line does not start DT earlycon immediately , instead it defers
* starting it until DT / ACPI decision is made . At that time if ACPI is enabled
* call parse_spcr ( ) , else call early_init_dt_scan_chosen_stdout ( )
*/
bool earlycon_init_is_deferred __initdata ;
2015-03-09 23:27:21 +03:00
/* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon ( char * buf )
{
int err ;
/*
* Just ' earlycon ' is a valid param for devicetree earlycons ;
* don ' t generate a warning from parse_early_params ( ) in that case
*/
2016-09-27 23:54:13 +03:00
if ( ! buf | | ! buf [ 0 ] ) {
if ( IS_ENABLED ( CONFIG_ACPI_SPCR_TABLE ) ) {
earlycon_init_is_deferred = true ;
return 0 ;
} else {
return early_init_dt_scan_chosen_stdout ( ) ;
}
}
2015-03-09 23:27:21 +03:00
err = setup_earlycon ( buf ) ;
2015-05-07 21:19:21 +03:00
if ( err = = - ENOENT | | err = = - EALREADY )
return 0 ;
2015-03-09 23:27:21 +03:00
return err ;
2015-03-09 23:27:20 +03:00
}
2015-03-09 23:27:21 +03:00
early_param ( " earlycon " , param_setup_earlycon ) ;
2015-03-09 23:27:20 +03:00
2016-01-17 02:23:38 +03:00
# ifdef CONFIG_OF_EARLY_FLATTREE
2016-01-17 02:23:44 +03:00
int __init of_setup_earlycon ( const struct earlycon_id * match ,
2016-01-17 02:23:43 +03:00
unsigned long node ,
2016-01-17 02:23:42 +03:00
const char * options )
2014-03-27 17:06:16 +04:00
{
int err ;
struct uart_port * port = & early_console_dev . port ;
2016-01-17 02:23:43 +03:00
const __be32 * val ;
bool big_endian ;
2016-01-17 02:23:44 +03:00
u64 addr ;
2014-03-27 17:06:16 +04:00
serial: earlycon: Add missing spinlock initialization
If an earlycon console driver needs to acquire the uart_port.lock
spinlock for serial console output, and CONFIG_DEBUG_SPINLOCK=y:
BUG: spinlock bad magic on CPU#0, swapper/0
lock: sci_ports+0x0/0x3480, .magic: 00000000, .owner: <none>/-1, .owner_cpu: 0
CPU: 0 PID: 0 Comm: swapper Not tainted 4.4.0-rc2-koelsch-g62ea5edf143bb1d0-dirty #2083
Hardware name: Generic R8A7791 (Flattened Device Tree)
[<c00173a0>] (unwind_backtrace) from [<c0013094>] (show_stack+0x10/0x14)
[<c0013094>] (show_stack) from [<c01f2338>] (dump_stack+0x70/0x8c)
[<c01f2338>] (dump_stack) from [<c00702d8>] (do_raw_spin_lock+0x20/0x190)
[<c00702d8>] (do_raw_spin_lock) from [<c0267590>] (serial_console_write+0x4c/0x130)
[<c0267590>] (serial_console_write) from [<c00734c4>] (call_console_drivers.constprop.13+0xc8/0xec)
[<c00734c4>] (call_console_drivers.constprop.13) from [<c0074ef0>] (console_unlock+0x354/0x440)
[<c0074ef0>] (console_unlock) from [<c0075bb4>] (register_console+0x2a0/0x394)
[<c0075bb4>] (register_console) from [<c06cb750>] (of_setup_earlycon+0x90/0xa4)
[<c06cb750>] (of_setup_earlycon) from [<c06cfb60>] (setup_of_earlycon+0x118/0x13c)
[<c06cfb60>] (setup_of_earlycon) from [<c06b34ac>] (do_early_param+0x64/0xb4)
[<c06b34ac>] (do_early_param) from [<c00472c0>] (parse_args+0x254/0x350)
[<c00472c0>] (parse_args) from [<c06b3860>] (parse_early_options+0x2c/0x3c)
[<c06b3860>] (parse_early_options) from [<c06b389c>] (parse_early_param+0x2c/0x40)
[<c06b389c>] (parse_early_param) from [<c06b5b08>] (setup_arch+0x520/0xaf0)
[<c06b5b08>] (setup_arch) from [<c06b3948>] (start_kernel+0x94/0x370)
[<c06b3948>] (start_kernel) from [<40008090>] (0x40008090)
Initialize the spinlock in of_setup_earlycon() and register_earlycon(),
to fix this for both DT-based and legacy earlycon. If the driver would
reinitialize the spinlock again, this is harmless, as it's allowed to
reinitialize an unlocked spinlock.
Alternatives are:
- Drivers having an early_serial_console_write() that only performs
the core functionality of serial_console_write(), without acquiring
the lock (which may be unsafe, depending on the hardware),
- Drivers initializing the spinlock in their private earlycon setup
functions.
As uart_port is owned by generic serial_core, and uart_port.lock is
initialized by uart_add_one_port() for the normal case, this can better
be handled in the earlycon core.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Reported-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-27 13:13:48 +03:00
spin_lock_init ( & port - > lock ) ;
2014-03-27 17:06:16 +04:00
port - > iotype = UPIO_MEM ;
2016-01-17 02:23:44 +03:00
addr = of_flat_dt_translate_address ( node ) ;
if ( addr = = OF_BAD_ADDR ) {
pr_warn ( " [%s] bad address \n " , match - > name ) ;
return - ENXIO ;
}
2014-03-27 17:06:16 +04:00
port - > mapbase = addr ;
port - > uartclk = BASE_BAUD * 16 ;
2016-01-17 02:23:44 +03:00
port - > membase = earlycon_map ( port - > mapbase , SZ_4K ) ;
2014-03-27 17:06:16 +04:00
2016-01-17 02:23:43 +03:00
val = of_get_flat_dt_prop ( node , " reg-offset " , NULL ) ;
if ( val )
port - > mapbase + = be32_to_cpu ( * val ) ;
val = of_get_flat_dt_prop ( node , " reg-shift " , NULL ) ;
if ( val )
port - > regshift = be32_to_cpu ( * val ) ;
big_endian = of_get_flat_dt_prop ( node , " big-endian " , NULL ) ! = NULL | |
( IS_ENABLED ( CONFIG_CPU_BIG_ENDIAN ) & &
of_get_flat_dt_prop ( node , " native-endian " , NULL ) ! = NULL ) ;
val = of_get_flat_dt_prop ( node , " reg-io-width " , NULL ) ;
if ( val ) {
switch ( be32_to_cpu ( * val ) ) {
case 1 :
port - > iotype = UPIO_MEM ;
break ;
case 2 :
port - > iotype = UPIO_MEM16 ;
break ;
case 4 :
port - > iotype = ( big_endian ) ? UPIO_MEM32BE : UPIO_MEM32 ;
break ;
default :
pr_warn ( " [%s] unsupported reg-io-width \n " , match - > name ) ;
return - EINVAL ;
}
}
2016-01-17 02:23:42 +03:00
if ( options ) {
strlcpy ( early_console_dev . options , options ,
sizeof ( early_console_dev . options ) ) ;
}
2016-01-17 02:23:41 +03:00
earlycon_init ( & early_console_dev , match - > name ) ;
2016-01-17 02:23:42 +03:00
err = match - > setup ( & early_console_dev , options ) ;
2014-03-27 17:06:16 +04:00
if ( err < 0 )
return err ;
if ( ! early_console_dev . con - > write )
return - ENODEV ;
register_console ( early_console_dev . con ) ;
return 0 ;
}
2016-01-17 02:23:38 +03:00
# endif /* CONFIG_OF_EARLY_FLATTREE */