/*
 * Jeff Harrell, jharrell@ti.com
 * Copyright (C) 2001 Texas Instruments, 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.
 *
 * Texas Instruments Sangam specific setup.
 */
#include <linux/config.h>
#include <asm/reboot.h>
#include <asm/avalanche/avalanche_map.h>
#include <asm/mips-boards/prom.h>
#include <asm/avalanche/sangam/hw_irq.h>
#include <asm/avalanche/sangam/hw_clock.h>
#include <asm/avalanche/sangam/hw_reset.h>
#include <asm/avalanche/sangam/hw_emif.h>
#include <asm/avalanche/sangam/hw_timer.h>
#include <asm/avalanche/sangam/hw_gpio.h>
#include <asm/avalanche/sangam/hw_boot.h>
 
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
struct _hw_boot *hw_boot = (struct _hw_boot *) AVALANCHE_DEVICE_CONFIG_LATCH_BASE;
struct EMIF_register_memory_map *hw_emif = (struct EMIF_register_memory_map *) AVALANCHE_EMIF_CONTROL_BASE;
struct _usb_hw *hw_usb = (struct _usb_hw *) AVALANCHE_USB_SLAVE_BASE;
struct _usbdma_hw *hw_usbdma = (struct _usbdma_hw *) AVALANCHE_USB_SLAVE_CONTROL_BASE;
struct _hw_gpio *hw_gpio = (struct _hw_gpio *) AVALANCHE_GPIO_BASE;
struct _hw_i2c *hw_i2c = (struct _hw_i2c *) AVALANCHE_I2C_BASE;
struct _irq_hw *hw_irq = (struct _irq_hw *) AVALANCHE_INTC_BASE;
union _hw_non_reset *hw_reset = (union _hw_non_reset *) AVALANCHE_RESET_CONTROL_BASE;
struct _timer_hw *hw_timer0 = (struct _timer_hw *) AVALANCHE_TIMER0_BASE;
struct _timer_hw *hw_timer1 = (struct _timer_hw *) AVALANCHE_TIMER1_BASE;
struct _vlynq_registers *hw_vlynq0 = (struct _vlynq_registers *) AVALANCHE_LOW_VLYNQ_CONTROL_BASE;
#if CONFIG_MIPS_OHIO == 0
struct _vlynq_registers *hw_vlynq1 = (struct _vlynq_registers *) AVALANCHE_HIGH_VLYNQ_CONTROL_BASE;
#endif /*--- #if CONFIG_MIPS_OHIO == 0 ---*/
struct _hw_clock *hw_clock = (struct _hw_clock *) AVALANCHE_CLOCK_CONTROL_BASE;

/*--- AVALANCHE_ADSL_SUB_SYS_MEM_BASE; ---*/
/*--- AVALANCHE_BROADBAND_INTERFACE__BASE; ---*/
/*--- AVALANCHE_ATM_SAR_BASE; ---*/
/*--- AVALANCHE_LOW_VLYNQ_MEM_MAP_BASE; ---*/
/*--- AVALANCHE_LOW_CPMAC_BASE; ---*/
/*--- AVALANCHE_WATCHDOG_TIMER_BASE; ---*/
/*--- AVALANCHE_UART0_REGS_BASE; ---*/
/*--- AVALANCHE_UART1_REGS_BASE; ---*/
/*--- AVALANCHE_MCDMA0_CTRL_BASE; ---*/
/*--- AVALANCHE_BIST_CONTROL_BASE; ---*/
/*--- AVALANCHE_MDIO_BASE; ---*/
/*--- AVALANCHE_FSER_BASE; ---*/
/*--- AVALANCHE_HIGH_CPMAC_BASE; ---*/
/*--- AVALANCHE_HIGH_VLYNQ_MEM_MAP_BASE; ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined (CONFIG_MIPS_AVALANCHE_VLYNQ) || defined (CONFIG_MIPS_AVALANCHE_VLYNQ_MODULE)
extern REMOTE_VLYNQ_DEV_RESET_CTRL_FN p_remote_vlynq_dev_reset_ctrl;
#endif /*--- #if defined (CONFIG_MIPS_AVALANCHE_VLYNQ) || defined (CONFIG_MIPS_AVALANCHE_VLYNQ_MODULE) ---*/

extern SET_MDIX_ON_CHIP_FN_T p_set_mdix_on_chip_fn;


#define AUTO_MDIX_ENABLE_PIN        30
#define MDIX_NORMAL_MODE            0

#define AUTO_MDIX_GPIO_PIN          28
#define MDIX_MODE                   1
#define MDI_MODE                    0

#define AUTO_MDIX_CNTL_MASK        ( (1 << AUTO_MDIX_ENABLE_PIN) \
                                    |(1 << AUTO_MDIX_GPIO_PIN))
/* We ignore MDIX_NORMAL_MODE as it is 0 and we get to set 0 from the below 
   macros for the AUTO_MDIX_ENABLE_PIN. */
#define AUTO_MDIX_ON_WORD          (MDIX_MODE << AUTO_MDIX_GPIO_PIN)
#define AUTO_MDIX_OFF_WORD         (MDI_MODE << AUTO_MDIX_GPIO_PIN) 

int sangam_set_mdix_on_chip(unsigned int base_addr, unsigned int operation)
{
    if((!p_set_mdix_on_chip_fn) || (base_addr != AVALANCHE_LOW_CPMAC_BASE))
        return (-1);

    if(operation)
        /* 0perating on bit 29 and 30. Writing 1 to bit 29 and 0 to bit 30. */
        avalanche_gpio_out_value(AUTO_MDIX_ON_WORD, AUTO_MDIX_CNTL_MASK,0);
    else
        /* 0perating on bit 29 and 30. Writing 0 to bit 29 and 0 to bit 30. */
        avalanche_gpio_out_value(AUTO_MDIX_OFF_WORD, AUTO_MDIX_CNTL_MASK,0);

    return (0); 
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
int avalanche_need(enum _avalanche_need_ need) {
    static char *pHWRevision = NULL;
    static unsigned int HWRevision = 0;
    static char *AutoMDIX = NULL;
    if(pHWRevision == NULL || HWRevision == 0) {
        pHWRevision = prom_getenv("HWRevision");
        prom_printf("HWRevision=\"%s\"\n", pHWRevision ? pHWRevision : "<unknown>");
        HWRevision = simple_strtol(pHWRevision , NULL, 0);
    }
    if(AutoMDIX == NULL) {
        AutoMDIX = prom_getenv("AutoMDIX");
        prom_printf("AutoMDIX=\"%s\"\n", AutoMDIX ? AutoMDIX : "<unknown>");
    }

    if(HWRevision == 0) {
        return 0;
    }
    switch(need) {
        case avalanche_need_vlynq:
            switch(HWRevision) {
                case 60: /*--- Fritz Box SL WLAN Annex B ---*/
                case 61: /*--- Fritz Box FON WLAN Annex B ---*/
                case 65: /*--- Fritz Box SL WLAN Annex A ---*/
                case 66: /*--- Fritz Box FON WLAN Annex A ---*/
                case 76: /*--- Fritz Box FON 2 WLAN ---*/
                case 77: /*--- Fritz Box FON 2 WLAN Annex A ---*/
                case 79: /*--- Fritz Box WLAN 3070 ---*/
                case 82: /*--- Fritz Box WLAN 3050 ---*/
                case 85: /*--- Fritz Box WLAN 3030 ---*/
                case 100: /*--- Fritz Box User ---*/
                    return 1;
                default:
                    break;
            }
            break;
        case avalanche_need_auto_mdix:
            switch(HWRevision) {
                case 60: /*--- Fritz Box SL WLAN Annex B ---*/
                case 61: /*--- Fritz Box FON WLAN Annex B ---*/
                case 65: /*--- Fritz Box SL WLAN Annex A ---*/
                case 66: /*--- Fritz Box FON WLAN Annex A ---*/
                case 71: /*--- Fritz Box FON ATA ---*/
                case 72: /*--- Fritz Box FON 2 ---*/
                case 73: /*--- Fritz Box FON 2 AnnexA ---*/
                case 74: /*--- Fritz Box 8MB ---*/
                case 75: /*--- Fritz Box MVoIP ---*/
                case 76: /*--- Fritz Box FON 2 WLAN ---*/
                case 77: /*--- Fritz Box FON 2 WLAN AnnexA ---*/
                case 78: /*--- Eumes 300IP ---*/
                case 79: /*--- Fritz Box WLAN 3070 ---*/
                /*--- case 80: ---*/ /*--- Fritz Box ---*/
                case 81: /*--- Fritz Box 301 ---*/
                case 82: /*--- Fritz Box WLAN 3050 ---*/
                case 83: /*--- Fritz Box 2030 ---*/
                case 84: /*--- Fritz Box 2070 ---*/
                case 85: /*--- Fritz Box WLAN 3030 ---*/
                /*--- case : ---*/ /*--- Fritz Box ---*/
                case 100: /*--- Fritz Box USER ---*/
                    return 1;
                default:
                    break;
            }
            if(AutoMDIX && (AutoMDIX[0] == 'y')) {
                return 1;
            }
            break;
    }
    return 0;
}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined (CONFIG_MIPS_AVALANCHE_VLYNQ)
void avalanche_soc_platform_init_vlynq(void) {
    if(avalanche_need(avalanche_need_vlynq)) {
        p_remote_vlynq_dev_reset_ctrl = remote_vlynq_dev_reset_ctrl;
        vlynq_dev_init();
    }
}
#endif /*--- #if defined (CONFIG_MIPS_AVALANCHE_VLYNQ) ---*/

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
void avalanche_soc_platform_init(void)
{
    printk("Reset Status: ");
    switch (avalanche_get_sys_last_reset_status())
    {
        case HARDWARE_RESET:
            printk("HW reset\n");
            break;
        case WATCHDOG_RESET:
            printk("WD reset\n");
            break;
        case SOFTWARE_RESET0:
            printk("SW reset 0\n");
            break;
        case SOFTWARE_RESET1:
            printk("SW reset 1\n");
            break;
        default:
            printk("unknown.\n");
    }
    
    /* GPIO INIT */
    avalanche_gpio_init();
    
    avalanche_reset_ctrl(AVALANCHE_UART0_RESET_BIT, OUT_OF_RESET);
    avalanche_reset_ctrl(AVALANCHE_GPIO_RESET_BIT, OUT_OF_RESET);

//    avalanche_gpio_out_value(0xf3fc3ff0, 0xf3fc3ff0, 0);
    /* hack for now, to be cleaned up. */
    *(unsigned int *)0xa861090c = 0xf3fc3ff0;

//    REG32_WRITE(TNETD73XX_GPIOENR, 0xf3fc3ff0);

    avalanche_reset_ctrl(AVALANCHE_LOW_EPHY_RESET_BIT,IN_RESET);

//#if (!defined(CONFIG_MIPS_AR7WRD) && !defined(CONFIG_MIPS_AR7VWI))
#if (!defined(CONFIG_MIPS_AVALANCHE_MARVELL))
    avalanche_reset_ctrl(AVALANCHE_LOW_EPHY_RESET_BIT,OUT_OF_RESET);
#endif

                                                                                       
    /* CLK CTRl INIT */                                                                
    avalanche_clkc_init(AFECLK_FREQ, REFCLK_FREQ, OSC3_FREQ);                   


   /* initialize vbus frequency */
    avalanche_set_vbus_freq(avalanche_clkc_get_freq(CLKC_VBUS));

	/* This initialization is required to make the kernel work with ADAM2. */
	{
    volatile unsigned long *reset_cntl = (volatile unsigned long *) AVALANCHE_RESET_CONTROL_BASE, 
                           *mdio_cntl  = (volatile unsigned long *)((int)AVALANCHE_MDIO_BASE + 0x4);		
		printk ("DEBUG: Bring MDIO out of reset.\n");

   *reset_cntl |= (1 << AVALANCHE_MDIO_RESET_BIT) | (1 << AVALANCHE_LOW_CPMAC_RESET_BIT) | (1 << AVALANCHE_HIGH_CPMAC_RESET_BIT) | (1 << AVALANCHE_LOW_EPHY_RESET_BIT);                                 
    *mdio_cntl   = (1 << 30) | (avalanche_get_vbus_freq()/2200000);    
	}

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if CONFIG_MIPS_OHIO == 1
    p_set_mdix_on_chip_fn = &sangam_set_mdix_on_chip;
    avalanche_gpio_ctrl(AUTO_MDIX_ENABLE_PIN, GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_ctrl(AUTO_MDIX_GPIO_PIN, GPIO_PIN, GPIO_OUTPUT_PIN);
    prom_printf("MDIX enabled.\n");
#else /*--- #if CONFIG_MIPS_OHIO == 1 ---*/
    /* Takes care of the CVR bug in the Sangam Chip as well. */
    if((*((volatile unsigned int*)AVALANCHE_CVR) == 0x570005) ||
       (*((volatile unsigned int*)AVALANCHE_CVR) == 0x200005) ||
       (*((volatile unsigned int*)AVALANCHE_CVR) == 0x210005) ||
       (*((volatile unsigned int*)AVALANCHE_CVR) == 0x220005) ||
       (*((volatile unsigned int*)AVALANCHE_CVR) == 0x230005)) {
        prom_printf("This SOC has MDIX cababilities on chip: ");
        if(avalanche_need(avalanche_need_auto_mdix)) {
            p_set_mdix_on_chip_fn = &sangam_set_mdix_on_chip;

            avalanche_gpio_ctrl(AUTO_MDIX_ENABLE_PIN, GPIO_PIN, GPIO_OUTPUT_PIN);
            avalanche_gpio_ctrl(AUTO_MDIX_GPIO_PIN, GPIO_PIN, GPIO_OUTPUT_PIN);
            prom_printf("MDIX enabled.\n");
        } else {
            prom_printf("MDIX disabled.\n");
        }

    } 
#endif /*--- #else ---*/ /*--- #if CONFIG_MIPS_OHIO == 1 ---*/
   
/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined (CONFIG_MIPS_AVALANCHE_VLYNQ)
    avalanche_soc_platform_init_vlynq();
#endif
}


