/*
 * Carsten Langgaard, carstenl@mips.com
 * Copyright (C) 2002 MIPS Technologies, Inc.  All rights reserved.
 *
 *  This program is free software; you can distribute it and/or modify it
 *  under the terms of the GNU General Public License (Version 2) as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope 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.
 *
 * UR8 specific setup.
 */
#include <linux/config.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/serial.h>
#include <linux/serial_core.h>

#include <asm/cpu.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/mips-boards/generic.h>
#include <asm/mips-boards/prom.h>
#include <asm/mips-boards/ur8.h>
#include <asm/mips-boards/ur8int.h>
#include <asm/mach_avm.h>
#include <asm/time.h>
#include <linux/delay.h>


/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#include <asm/mach-ur8/hw_bbif.h>
#include <asm/mach-ur8/hw_boot.h>
#include <asm/mach-ur8/hw_clock.h>
#include <asm/mach-ur8/hw_emif.h>
#include <asm/mach-ur8/hw_gpio.h>
#include <asm/mach-ur8/hw_i2c.h>
#include <asm/mach-ur8/hw_irq.h>
#include <asm/mach-ur8/hw_mdio.h>
#include <asm/mach-ur8/hw_reset.h>
#include <asm/mach-ur8/hw_stack.h>
#include <asm/mach-ur8/hw_timer.h>
#include <asm/mach-ur8/hw_uart.h>
#include <asm/mach-ur8/hw_usb.h>

struct _hw_boot *UR8_hw_boot  = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE;
struct _hw_clock *UR8_hw_clock  = (struct _hw_clock *)UR8_CLOCK_BASE;
struct EMIF_register_memory_map *UR8_EMIF_register_memory_map = (struct EMIF_register_memory_map *)UR8_EMIF_BASE;
struct _hw_gpio *UR8_hw_gpio  = (struct _hw_gpio *)UR8_GPIO_BASE;
/*--- struct _hw_i2c *UR8_hw_i2c = (struct _hw_i2c *)UR8_I2C_BASE; ---*/
struct _irq_hw *UR8_irq_hw = (struct _irq_hw *)UR8_IRQ_CTRL_BASE;
union _hw_non_reset *UR8_hw_non_reset = (union _hw_non_reset *)UR8_RESET_BASE;
struct _timer_hw *UR8_timer_hw[2] = { (struct _timer_hw  *)UR8_TIMER1_BASE, (struct _timer_hw  *)UR8_TIMER1_BASE };
struct _hw_uart *UR8_hw_uart = (struct _hw_uart *)UR8_UART0_BASE;
struct _usb_hw *UR8_usb_hw = (struct _usb_hw *)UR8_USB_BASE;
/*--- struct _usbdma_hw *UR8_usbdma_hw = (struct _usbdma_hw *)UR8_USBDMA_BASE; ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined(CONFIG_VLYNQ_SUPPORT)
#include <asm/mach-ur8/hw_vlynq.h>
#include <asm/mips-boards/ur8_vlynq.h>
struct _vlynq_registers *UR8_vlynq_registers = (struct _vlynq_registers *)UR8_VLYNQ0_CTRL_BASE;
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/

#if defined(CONFIG_PCI)
#include <asm/mach-ur8/hw_pci.h>
#include <asm/mips-boards/ur8_pci.h>
struct _pci_registers *UR8_pci_registers = (struct _pci_registers *)UR8_PCI_CFG_BASE;
#endif /*--- #if defined(CONFIG_PCI) ---*/



/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
extern void mips_reboot_setup(void);
extern void mips_time_init(void);
extern void mips_timer_setup(struct irqaction *irq);
extern void ur8_clk_init(void);

static void __init ur8_serial_init(void);

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
const char *get_system_type(void) {
	return "MIPS UR8";
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int __init ur8_setup(void) {
	ioport_resource.end = 0x7fffffff;
    /*--- { ---*/
        /*--- unsigned char test[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"; ---*/

        /*--- printk("%% 12B:  % 12B\n", test); ---*/
        /*--- printk("%% 12.2B:  % 12.2B\n", test); ---*/
        /*--- printk("%%# 12.4B:  %# 12.4B\n", test); ---*/
        /*--- printk("little endian %%#- 12.4B:  %#- 12.4B\n", test); ---*/
        /*--- printk("big endian %%#+- 12.4B:  %#+- 12.4B\n", test); ---*/
        /*--- printk("%%-:12B:  %-:12B\n", test); ---*/
    /*--- } ---*/

    ur8_reset_init();
    ur8_clk_init();
    ur8_serial_init();
#if defined(CONFIG_VLYNQ_SUPPORT)
    ur8_vlynq_init();
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/
        ur8_gpio_init();
	board_time_init = mips_time_init;
	board_timer_setup = mips_timer_setup;
	mips_reboot_setup();
    return 0;
}
early_initcall(ur8_setup);

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static void __init ur8_serial_init(void) {
#ifdef CONFIG_SERIAL_8250
	struct uart_port s;

	prom_printf("[ur8_serial_init]\n");
	memset(&s, 0, sizeof(s));

#ifdef CONFIG_CPU_LITTLE_ENDIAN
	/*--- s.membase   = CPHYSADDR(UR8_UART0_BASE); ---*/
	s.membase   = (unsigned char *)CKSEG1ADDR(UR8_UART0_BASE);
#else
	s.membase   = CPHYSADDR(UR8_UART0_BASE+3);
#endif
	s.irq      = UR8INT_UART0;
	s.uartclk  = ur8_get_clock(avm_clock_id_peripheral);
	/*--- s.fifosize = 1; ---*/
	s.fifosize = 16;
	s.x_char   = 'S' - ' ';
	/*--- s.flags    = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; ---*/
	s.flags    = 0;
	s.line     = 0; /*--- erster UART ---*/
	s.type     = PORT_UR8; 
	s.iotype   = UPIO_MEM32;
	s.regshift = 2;

	if (early_serial_setup(&s) != 0) {
		printk(KERN_ERR "Serial setup failed!\n");
	}	
#endif
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined(CONFIG_UR8_CLOCK_SWITCH)
unsigned int ur8_pre_init_system_clock_change(enum _avm_clock_id clock_id, unsigned int new_clk) {
    struct _hw_uart *U = (struct _hw_uart *)UR8_UART0_BASE;
    unsigned int baud = ((new_clk / 2 / 38400) + 8) >> 4;

    /*--- printk("[ur8_pre_init_system_clock_change] called new_clk = %u start\n", new_clk); ---*/

    U->lc.Register |=  (1 << 7);  /* dlab bit setzen */
    U->data.tx.data = baud;
    U->ie.Register  = baud >> 8;
    U->lc.Register &= ~(1 << 7);  /* dlab bit setzen */

    /*--- printk("[ur8_pre_init_system_clock_change] called new_clk = %u done\n", new_clk); ---*/
    return 0;
}

#endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int ur8_late_init(void) {
#if defined(CONFIG_UR8_CLOCK_SWITCH)
    ur8_get_clock_notify(avm_clock_id_system, ur8_pre_init_system_clock_change);
#endif /*--- #if defined(CONFIG_UR8_CLOCK_SWITCH) ---*/
    return 0;
}

late_initcall(ur8_late_init);

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
// Aufruf in init des Kernels
void ur8_pre_init(void) {
    volatile struct _hw_uart *U = (struct _hw_uart *)UR8_UART0_BASE;
    volatile struct _hw_gpio *G = (struct _hw_gpio *)UR8_GPIO_BASE;
    volatile union _hw_non_reset *R = (union _hw_non_reset *)UR8_RESET_BASE;
    volatile struct _hw_clock *C = (struct _hw_clock *)UR8_CLOCK_BASE;
    volatile struct _hw_boot *BOOT  = (struct _hw_boot *)UR8_DEVICE_CONFIG_BASE;
    unsigned int baud = ((ur8_get_clock(avm_clock_id_peripheral) / 38400) + 8) >> 4;

    prom_printf("[ur8_pre_init] System Clk = %u Hz               \n", ur8_get_clock(avm_clock_id_peripheral));
    
    C->PDCR.Bits.uartp = 0;
    C->PDCR.Bits.gpiop = 0;
    C->PDCR.Bits.pcip = 0;
    C->PDCR.Bits.mcbspp = 0;
    prom_printf("PDCR %p: %x\n", &C->PDCR.Register, C->PDCR.Register);

    R->Bits.uart0_unreset = 1;
    R->Bits.gpio_unreset = 1;
    /*--- R->Bits.tdm_unreset = 1; ---*/
    prom_printf("RESET_PRCR %p: %x\n", &R->Reg, R->Reg);

    G->Direction.Bits.uart0_rd = 0;  /*--- output ---*/
    G->Direction.Bits.uart0_td = 0;  /*--- output ---*/
    G->Enable.Bits.uart0_rd = 0;  /*--- funktion ---*/
    G->Enable.Bits.uart0_td = 0;  /*--- funktion ---*/

#ifdef CONFIG_UR8_EVM 
//evm
    prom_printf("[kernel for TI-EVM]\n");
    G->Direction2.Bits.preq1 = 1; /*--- input ---*/  // 38
    G->Enable2.Bits.preq1 = 0; /*--- function ---*/  // 38
#else 
//7270
    prom_printf("[kernel for AVM Board]\n");
    G->Direction2.Bits.preq1 = 0; /*--- output ---*/  // 38
    G->Enable2.Bits.preq1 = 1; /*--- gpio ---*/  // 38
    prom_printf("Before: Output->Preq1: %i\n",G->OutputData2.Bits.preq1);
    prom_printf("Before: Input->Preq1: %i\n",G->InputData2.Bits.preq1);

    udelay(2000);
    G->OutputData2.Bits.preq1 = 1; /*--- enable ---*/  // 38
    prom_printf("After: Output->Preq1: %i\n",G->OutputData2.Bits.preq1);
    prom_printf("After: Input->Preq1: %i\n",G->InputData2.Bits.preq1);
#endif

    G->Direction2.Bits.preq2 = 1; /*--- input ---*/   
    G->Enable2.Bits.preq2 = 0; /*--- function ---*/   

    G->Direction.Bits.pintc = 0; /*--- output ---*/
    G->Enable.Bits.pintc = 0;  /*--- function ---*/

    G->Direction.Bits.pgnt1 = 0; /*--- output ---*/
    G->Enable.Bits.pgnt1 = 0;  /*--- function ---*/
    G->Direction.Bits.pgnt2 = 0; /*--- output ---*/
    G->Enable.Bits.pgnt2 = 0;  /*--- function ---*/
    G->Direction.Bits.pintb = 0; /*--- output ---*/
    G->Enable.Bits.pintb = 0;  /*--- function ---*/
#if 0 
    /*--- for PCM-bus (pcmlink): ---*/
    /*--- prom_printf("[pcmlink: set tdm-pins]\n"); ---*/
    R->Bits.mcsp_unreset = 1;
    G->Direction.Bits.tdmrd  = GPIO_INPUT_PIN;
    G->Enable.Bits.tdmrd     = FUNCTION_PIN;
    G->Direction.Bits.tdmtd  = GPIO_OUTPUT_PIN;
    G->Enable.Bits.tdmtd     = FUNCTION_PIN;
    G->Direction.Bits.tdmclk = GPIO_INPUT_PIN;
    G->Enable.Bits.tdmclk    = FUNCTION_PIN;
    G->Direction.Bits.tdmfs  = GPIO_INPUT_PIN;
    G->Enable.Bits.tdmfs     = FUNCTION_PIN;

    BOOT->tdmselreg          = 0; /*--- MCSP ---*/   
    prom_printf("TDM_SEL %p: %x\n", &BOOT->tdmselreg, BOOT->tdmselreg);
#endif
    U->lc.Bits.ws = 3;   /*--- 8 Bit ---*/
    U->lc.Bits.dlab = 1;
    U->data.tx.data = baud;
    U->ie.Register = baud >> 8;
    U->lc.Bits.dlab = 0;

	board_time_init = mips_time_init;
	board_timer_setup = mips_timer_setup;

    memset((char *)0xA0000000, 0, 0x4096);  /*--- internal ram loeschen ---*/
}

