/****************************************************************************
 *
 * Copyright (C) 2003, University of New South Wales
 *
 * File path:	platform/ofpower4/prom.cc
 * Description:	OpenFirmware Power4 Setup.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: prom.cc,v 1.3 2005/01/19 12:53:18 cvansch Exp $
 *
 ***************************************************************************/

#include INC_ARCH(of1275.h)
#include INC_ARCH(rtas.h)
#include INC_ARCH(1275tree.h)
#include INC_ARCH(ppc64_registers.h)
#include INC_GLUE(hwspace.h)
#include INC_API(kernelinterface.h)

#include <debug.h>

word_t boot_cpuid SECTION(".init.data");
word_t boot_cpukhz SECTION(".init.data");
word_t boot_buskhz SECTION(".init.data");

/*
 * Map the position-independent device tree, and install.
 */
SECTION(".init") void of1275_tree_map( addr_t low, addr_t high )
{
    addr_t vaddr = phys_to_virt(low);

    prom_print_hex( "1275 tree found at", (word_t)vaddr );
    prom_puts( "\n\r" );

    PTRRELOC(get_of1275_tree())->init( (char *)vaddr );
}


/*
 * Finds and installs the position-independent copy of the
 * OpenFirmware device tree.
 */
SECTION(".init") void of1275_tree_init( kernel_interface_page_t *kip )
{
    // Look for the position-independent copy of the OpenFirmware device tree
    // in the kip's memory descriptors.
    for( word_t i = 0; i < kip->memory_info.get_num_descriptors(); i++ ) 
    {
	memdesc_t *mdesc = kip->memory_info.get_memdesc( i );
	if( (mdesc->type() == OF1275_KIP_TYPE) && 
		(mdesc->subtype() == OF1275_KIP_SUBTYPE) )
	{
	    of1275_tree_map( mdesc->low(), mdesc->high() );
	    return;
	}
    }

    // Not found.  Things won't work, but ...
    prom_puts( "*** Error: the boot loader didn't supply a copy of the\n\r"
	       "*** Open Firmware device tree!\n\r" );
    PTRRELOC(get_of1275_tree())->init( NULL );
}


/* Initialise the Platform
 * We are called at the kernel PHYSICAL address, relocate!
 * Map the kernel so that normal operation can continue
 */
void SECTION(".init") init_plat( word_t ofentry )
{
    /* Initialise the Open Firmware interface used to setup the RTAS */
    of1275_client_interface_t *of = PTRRELOC(get_of1275());
    kernel_interface_page_t *kip = PTRRELOC(get_kip());

    of->init(ofentry);

    /* Initialise position independant the device tree */
    of1275_tree_init( kip );

#ifndef CONFIG_PLAT_OFG5
    /* Initialise the RTAS */
    get_rtas()->init_arch();
#endif

    word_t pvr = ppc64_get_pvr();
    word_t cpu = (pvr>>16) & 0xffff;

    if (cpu == 0x35) prom_puts( "Detected Power4 (Spinnaker) " );
    else if (cpu == 0x38) prom_puts( "Detected Power4+	" );
    else if (cpu == 0x39) prom_puts( "Detected PPC970	" );
    else if (cpu == 0x3c) prom_puts( "Detected PPC970FX	" );
    else if (cpu == 0x40) prom_puts( "Detected Power3	" );
    else if (cpu == 0x41) prom_puts( "Detected Power3+	" );
    else {
	prom_print_hex( "Unknown Processor Version", (pvr >> 16) & 0xffff );
	prom_exit( "\n\r" );
    }
    prom_print_hex( "Revision", (pvr & 0xffff) );
    prom_puts( "\n\r" );

    u32_t *prop_val, len, cpuid, cpu_hz, bus_hz;
    of1275_device_t *chosen = PTRRELOC(get_of1275_tree())->find( "/chosen" );

    if ( !chosen->get_prop( "cpu", (char **)&prop_val, &len ))
    {
	prom_exit( "Unable get property \"cpu\" in /chosen\n\r" );
    }
    of1275_phandle_t cpu_pkg = *prop_val;
//    of1275_phandle_t cpu_pkg = of->instance_to_package( *prop_val );
    of->get_prop( cpu_pkg, "reg", &cpuid, sizeof(cpu));

    of->get_prop( cpu_pkg, "clock-frequency", &cpu_hz, sizeof(cpu_hz));
    of->get_prop( cpu_pkg, "bus-frequency", &bus_hz, sizeof(bus_hz));
    
    boot_cpuid = cpuid;
    boot_cpukhz = cpu_hz/1000;
    boot_buskhz = bus_hz/1000;

    prom_print_hex( "Boot cpu is", boot_cpuid );
    prom_puts( "\n\r" );
}