/*
 * arch/ppc/platforms/sorcery.c
 *
 * Based on board setup routines for the Alaska (MPC8220i-RPC) board.
 * Copyright 2005 DENX Software Engineering
 * Wolfgang Denk wd@denx.de
 *
 * Original Author: MontaVista Software, Inc.
 *	source@mvista.com
 *
 * Based on Glacier version by Dale Farnsworth <dale.farnsworth@mvista.com>
 * with modifications for Alaska by Kent Borg and alaska_pin_mux.c
 * by Peter Barada.
 *
 * Copyright 2003 Motorola Inc.
 * Copyright 2003-2004 MontaVista, Software, Inc.
 *
 * 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/config.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/reboot.h>
#include <linux/pci.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/blk.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/seq_file.h>
#include <linux/ide.h>

#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/pci-bridge.h>
#include <asm/ppcboot.h>

#include <platforms/sorcery.h>

#ifdef CONFIG_BESTDMA
#include <mcdapi/MCD_dma.h>
#include <8220DmaApi.h>
#endif

unsigned char _res[sizeof(bd_t)];

u8 mpc8220_fec1_mac_addr[6];
u8 mpc8220_fec2_mac_addr[6];

static int
sorcery_show_cpuinfo(struct seq_file *m)
{
	seq_printf(m, "Vendor\t\t: Xerox\n");
	seq_printf(m, "Machine\t\t: Sorcery-C\n");

	return 0;
}

static int
sorcery_show_percpuinfo(struct seq_file *m, int i)
{
	/* Note that the PCI clock is the same as the input clock */
	bd_t *bd = (bd_t *)_res;

	seq_printf(m,
		"Input clock\t: %d.%02d MHz\n"
		"XLB clock\t: %d.%02d MHz\n"
		"Flexbus clock\t: %d.%02d MHz\n"
		"PCI clock\t: %d.%02d MHz\n"
		"Core clock\t: %d.%02d MHz\n"
		"VCO clock\t: %d.%02d MHz\n",
		(int) (bd->bi_inpfreq / 1000000),
		(int)((bd->bi_inpfreq / 10000) % 100),
		(int) (bd->bi_busfreq / 1000000),
		(int)((bd->bi_busfreq / 10000) % 100),
		(int) (bd->bi_flbfreq / 1000000),
		(int)((bd->bi_flbfreq / 10000) % 100),
		(int) (bd->bi_pcifreq / 1000000),
		(int)((bd->bi_pcifreq / 10000) % 100),
		(int) (bd->bi_intfreq / 1000000),
		(int)((bd->bi_intfreq / 10000) % 100),
		(int) (bd->bi_vcofreq / 1000000),
		(int)((bd->bi_vcofreq / 10000) % 100));

	return 0;
}

static void __init
sorcery_setup_arch(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
	if (initrd_start)
		ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */
	else
#endif
#ifdef CONFIG_ROOT_NFS
		ROOT_DEV = to_kdev_t(0x00ff); /* /dev/nfs pseudo device */
#else
		ROOT_DEV = to_kdev_t(0x0802); /* /dev/sda2 */
#endif
	printk(KERN_INFO "Xerox Sorcery-C port by DENX Software Engineering\n");

#if 0 /* def CONFIG_PCI */
	sorcery_find_bridges();
#endif
}

static void __init
sorcery_calibrate_decr(void)
{
	int freq, divisor;
	bd_t *bd = (bd_t *)_res;

	freq = bd->bi_busfreq;
	printk(KERN_INFO "Using XLB clock (%d.%02d MHz) to set up decrementer\n",
	       freq / 1000000, (freq / 10000) %100 );
	divisor = 4;
	tb_ticks_per_jiffy = freq / HZ / divisor;
	tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
}

static void
sorcery_restart(char *cmd)
{
	__cli();

	/* TBD: Watchdog needs to be used there */

	while (1);
}

static void
sorcery_halt(void)
{
	__cli();
	while (1);
}

static void
sorcery_power_off(void)
{
	sorcery_halt();
}

/*
 * Set BAT 3 to map 0xf0000000 to end of physical memory space.
 */
static __inline__ void
sorcery_set_bat(void)
{
	mb();
	mtspr(DBAT3U, 0xf0001ffe);
	mtspr(DBAT3L, 0xf000002a);
	mb();
}

#ifdef CONFIG_SERIAL_TEXT_DEBUG

#if CONFIG_MPC8220_PSC_CONSOLE_PORT == 0
#define SORCERY_PSC_CONSOLE MPC8220_PSC
#else
#error "mpc8220 PSC for console not selected"
#endif

void
sorcery_progress(char *s, unsigned short hex)
{
	struct mpc8220_psc *psc = (struct mpc8220_psc *)SORCERY_PSC_CONSOLE;
	char c;

	while ((c = *s++) != 0) {
		if (c == '\n') {
			while (!(in_be16(&psc->mpc8220_psc_status) &
				MPC8220_PSC_SR_TXRDY));
			out_8(&psc->mpc8220_psc_buffer_8, '\r');
		}
		while (!(in_be16(&psc->mpc8220_psc_status)
			& MPC8220_PSC_SR_TXRDY));
		out_8(&psc->mpc8220_psc_buffer_8, c);
	}
	while (!(in_be16(&psc->mpc8220_psc_status)
		& MPC8220_PSC_SR_TXRDY));
	out_8(&psc->mpc8220_psc_buffer_8, '\n');
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */

/*
 * We need to read the memory controller
 * to determine this value (bd_t can be used)
 *
 * Assumption: memory starts at 0x0
 */
static unsigned long __init
sorcery_find_end_of_memory(void)
{
	unsigned long total = 0;
	u32 cs_cfg;

	cs_cfg = in_be32((u32 *)MPC_CS0CFG) & 0x1f;
	if (cs_cfg >= 19) {
		total = 2 << cs_cfg;
	}

	cs_cfg = in_be32((u32 *)MPC_CS1CFG) & 0x1f;
	if (cs_cfg >= 19) {
		total += 2 << cs_cfg;
	}

	cs_cfg = in_be32((u32 *)MPC_CS2CFG) & 0x1f;
	if (cs_cfg >= 19) {
		total += 2 << cs_cfg;
	}

	printk(KERN_INFO "Total memory in system: %3ld MB\n", total>>20);

	return total;
}

static void __init
sorcery_map_io(void)
{
	io_block_mapping(MPC8220_MBAR, MPC8220_MBAR, 0x10000000, _PAGE_IO);

#if 0
	io_block_mapping(SORCERY_PCI_SPACE_IO, SORCERY_PCI_SPACE_IO,
		(SORCERY_PCI_SPACE_IO_END - SORCERY_PCI_SPACE_IO + 1), _PAGE_IO);
#endif
}

void __init
platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
	unsigned long r6, unsigned long r7)
{
	parse_bootinfo(find_bootinfo());

	/* ASSUMPTION:	If both r3 (BootParams pointer) and r6 (cmdline pointer)
	 * are non-zero, then we should use the board info from the BootParams
	 * structure and the cmdline pointed to by r6 instead of the
	 * information from birecs, if any.  Otherwise, use the information
	 * from birecs as discovered by the preceeding call to
	 * parse_bootinfo().  This rule should work with both U-Boot, which
	 * uses a BootParams board info structure, and the kernel boot wrapper,
	 * which uses birecs.
	 */
	if ( r3 && r6) {
		/* copy board info structure */
		memcpy( (void *)_res,(void *)(r3+KERNELBASE), sizeof(bd_t) );

		/* copy command line */
		*(char *)(r7+KERNELBASE) = 0;
		strcpy(cmd_line, (char *)(r6+KERNELBASE));
	}
	sorcery_set_bat();

#ifdef CONFIG_BLK_DEV_INITRD
	if (r4) {
		initrd_start = r4 + KERNELBASE;
		initrd_end = r5 + KERNELBASE;
	}
#endif

#if 0
	isa_io_base		  = SORCERY_ISA_IO_BASE;
	isa_mem_base		  = SORCERY_ISA_MEM_BASE;
	pci_dram_offset		  = SORCERY_PCI_DRAM_OFFSET;
#endif

	ppc_md.setup_arch	  = sorcery_setup_arch;
	ppc_md.show_cpuinfo	  = sorcery_show_cpuinfo;
	ppc_md.show_percpuinfo	  = sorcery_show_percpuinfo;
	ppc_md.init_IRQ		  = mpc8220_init_irq;
	ppc_md.get_irq		  = mpc8220_get_irq;

	ppc_md.find_end_of_memory = sorcery_find_end_of_memory;
	ppc_md.setup_io_mappings  = sorcery_map_io;

	ppc_md.restart		  = sorcery_restart;
	ppc_md.power_off	  = sorcery_power_off;
	ppc_md.halt		  = sorcery_halt;

	ppc_md.time_init	  = NULL;
	ppc_md.get_rtc_time	  = NULL;
	ppc_md.set_rtc_time	  = NULL;
	ppc_md.calibrate_decr	  = sorcery_calibrate_decr;

#ifdef CONFIG_SERIAL_TEXT_DEBUG
	ppc_md.progress		  = sorcery_progress;
#else
	ppc_md.progress		  = NULL;
#endif
	ppc_md.init		  = NULL;

#ifdef CONFIG_BESTDMA
	initDma();
#endif
}
