/*
 * 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.
 *
 * AR7 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/ar7.h>
#include <asm/mips-boards/ar7int.h>
#include <asm/mach_avm.h>
#include <asm/time.h>


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

struct _hw_boot *AR7_hw_boot  = (struct _hw_boot *)AR7_DEVICE_CONFIG_BASE;
struct _hw_clock *AR7_hw_clock  = (struct _hw_clock *)AR7_CLOCK_BASE;
struct EMIF_register_memory_map *AR7_EMIF_register_memory_map = (struct EMIF_register_memory_map *)AR7_EMIF_BASE;
struct _hw_gpio *AR7_hw_gpio  = (struct _hw_gpio *)AR7_GPIO_BASE;
struct _hw_i2c *AR7_hw_i2c = (struct _hw_i2c *)AR7_I2C_BASE;
struct _irq_hw *AR7_irq_hw = (struct _irq_hw *)AR7_IRQ_CTRL_BASE;
union _hw_non_reset *AR7_hw_non_reset = (union _hw_non_reset *)AR7_RESET_BASE;
struct _timer_hw *AR7_timer_hw[2] = { (struct _timer_hw  *)AR7_TIMER1_BASE, (struct _timer_hw  *)AR7_TIMER1_BASE };
struct _hw_uart *AR7_hw_uart = (struct _hw_uart *)AR7_UART0_BASE;
struct _usb_hw *AR7_usb_hw = (struct _usb_hw *)AR7_USB_BASE;
struct _usbdma_hw *AR7_usbdma_hw = (struct _usbdma_hw *)AR7_USBDMA_BASE;

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined(CONFIG_VLYNQ_SUPPORT)
#include <asm/mach-ar7/hw_vlynq.h>
#include <asm/mips-boards/ar7_vlynq.h>
struct _vlynq_registers *AR7_vlynq_registers = (struct _vlynq_registers *)AR7_VLYNQ0_CTRL_BASE;
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/

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


static void __init ar7_serial_init(void);

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

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int __init ar7_setup(void) {
	ioport_resource.end = 0x7fffffff;

    prom_printf("[ar7_setup]\n");
    /*--- { ---*/
        /*--- 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); ---*/
    /*--- } ---*/

    ar7_reset_init();
    ar7_clk_init();
	ar7_serial_init();
#if defined(CONFIG_VLYNQ_SUPPORT)
    /*--- ar7_vlynq_init(); ---*/
#endif /*--- #if defined(CONFIG_VLYNQ_SUPPORT) ---*/
    ar7_gpio_init();

	board_time_init = mips_time_init;
	board_timer_setup = mips_timer_setup;

	mips_reboot_setup();
    return 0;
}

early_initcall(ar7_setup);

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

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

#ifdef CONFIG_CPU_LITTLE_ENDIAN
	/*--- s.membase   = CPHYSADDR(AR7_UART0_BASE); ---*/
	s.membase   = (unsigned char *)CKSEG1ADDR(AR7_UART0_BASE);
#else
	s.membase   = CPHYSADDR(AR7_UART0_BASE+3);
#endif
	s.irq      = AR7INT_UART0;
	s.uartclk  = ar7_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_OHIO;  /* auch AR7 */
	s.iotype   = UPIO_MEM32;
	s.regshift = 2;

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

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

    printk("[ar7_pre_init_system_clock_change] called new_clk = %u start\n", new_clk);

    U->lc.Bits.dlab =  1 ;  /* dlab bit setzen */
    U->data.tx.data = baud;
    U->ie.Register  = baud >> 8;
    U->lc.Bits.dlab =  0; 

    printk("[ar7_pre_init_system_clock_change] called new_clk = %u done\n", new_clk);
    return 0;
}

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

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
static int ar7_late_init(void) {
    printk("[ar7_late_init] \n");
#if defined(CONFIG_AR7_CLOCK_SWITCH)
    ar7_get_clock_notify(avm_clock_id_system, ar7_pre_init_system_clock_change);
#endif /*--- #if defined(CONFIG_AR7_CLOCK_SWITCH) ---*/
    return 0;
}

late_initcall(ar7_late_init);

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void ar7_pre_init(void) {
    struct _hw_uart *U = (struct _hw_uart *)AR7_UART0_BASE;
    struct _hw_gpio *G = (struct _hw_gpio *)AR7_GPIO_BASE;
    union _hw_non_reset *R = (union _hw_non_reset *)AR7_RESET_BASE;
    struct _hw_clock *C = (struct _hw_clock *)AR7_CLOCK_BASE;
    unsigned int baud = ((ar7_get_clock(avm_clock_id_peripheral) / 38400) + 8) >> 4;

    prom_printf("[ar7_pre_init] PERIPHERAL Clk = %u Hz baud=%u        \n", ar7_get_clock(avm_clock_id_peripheral), baud);
    C->PDCR.Bits.ut0p = 0;
    C->PDCR.Bits.gpiop = 0;
    R->Bits.uart0_unreset = 1;
    R->Bits.gpio_unreset = 1;
    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 ---*/
    
    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, 4096);  /*--- internal ram loeschen ---*/
}

