/*
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/reboot.h>
#include <linux/pci.h>
#include <pnx833x.h>
#include <gpio.h>

#include <prjconfig.h>

extern void pnx833x_board_setup(void);
extern void pnx833x_machine_restart(char *);
extern void pnx833x_machine_halt(void);
extern void pnx833x_machine_power_off(void);

static inline unsigned int get_cpu_clock_mhz (void)
{
	return 90 + (PNX8335_REGFIELD(CLOCK_PLL_CPU_CTL, FREQ) * 10);
}

static inline void set_cpu_clock_mhz (unsigned int cpu_mhz)
{
	unsigned int temp;

	cpu_mhz = (cpu_mhz < 90) ? 90 : cpu_mhz;
	cpu_mhz = (cpu_mhz > 400) ? 400 : cpu_mhz;
	temp = (cpu_mhz - 90) / 10; /* map 90..400 to 0..31 */

	PNX8335_WRITEFIELD(temp, CLOCK_PLL_CPU_CTL, FREQ);
}

int __init plat_mem_setup (void)
{
	/* fake pci bus to avoid bounce buffers */
	PCI_DMA_BUS_IS_PHYS = 1;

#if defined (CONFIG_SOC_PNX8335) && ! defined (CONFIG_PNX8335_AUDIO_MIPS)

#if defined (CONFIG_CPU_CLK_MHZ_LINUX)
	if (get_cpu_clock_mhz() != CONFIG_CPU_CLK_MHZ_LINUX)
		set_cpu_clock_mhz (CONFIG_CPU_CLK_MHZ_LINUX);
#endif

	gpio_irq_init();
	pnx833x_board_setup();
#endif

	_machine_restart = pnx833x_machine_restart;
	_machine_halt = pnx833x_machine_halt;
	pm_power_off = pnx833x_machine_power_off;

	/* IO/MEM resources. */
	set_io_port_base(KSEG1);
	ioport_resource.start = 0;
	ioport_resource.end = ~0;
	iomem_resource.start = 0;
	iomem_resource.end = ~0;

	return 0;
}

/*
   This code is assumed to run after plat_mem_setup()...
*/
void __init plat_time_init (void)
{
	/* calculate mips_hpt_frequency based on PNX833X_CLOCK_CPUCP_CTL reg */

	extern unsigned long mips_hpt_frequency;
	unsigned int reg = PNX833X_CLOCK_CPUCP_CTL;
	unsigned int cpu_mhz;

	if (!(PNX833X_BIT(reg, CLOCK_CPUCP_CTL, EXIT_RESET)))
	{
		/* Functional clock is disabled so use crystal frequency */
		cpu_mhz = 25;
	}
	else
	{
#if defined(CONFIG_SOC_PNX8335)
		/* Functional clock is enabled, so get clock multiplier */
		cpu_mhz = get_cpu_clock_mhz();
#else
		static const unsigned long int freq[4] = {240, 160, 120, 80};
		cpu_mhz = freq[PNX833X_FIELD(reg, CLOCK_CPUCP_CTL, DIV_CLOCK)];
#endif
	}

	printk ("CPU clock is %u MHz\n", cpu_mhz);

	/*
	   C0 timer0 runs at half the CPU core clock
	*/
	mips_hpt_frequency = cpu_mhz * 500000;
}

