/*
 * linux/arch/arm/mach-dm320/davinci_vlynq_setup.c
 *
 * Setup the vlynq on the davinci evm.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Original Version:  Eyal Reizer, Texas Instruments Inc
 *
 */
 
#include "davinci_vlynq_setup.h"
#include <asm/cacheflush.h>
#include "../clock.h"

#ifdef PSP_KERNEL_03
#include <asm/arch/i2c-expander.h>
#warning "i2c PSP client"
#else
#warning "i2c client"
#include <asm/arch/i2c-client.h>
#endif

#define PLL1_PLLM   __REG(0x01c40910)
#define PLL2_PLLM   __REG(0x01c40D10)
#define PTCMD       __REG(0x01C41120)
#define PDSTAT      __REG(0x01C41200)
#define PDCTL1      __REG(0x01C41304)
#define EPCPR       __REG(0x01C41070)
#define PTSTAT      __REG(0x01C41128)
                                                                                
#define MDSTAT  IO_ADDRESS(0x01C41800)
#define MDCTL   IO_ADDRESS(0x01C41A00)
#define VDD3P3V_PWDN  __REG(0x01C40048)

static char *wlan_reset = "high";
MODULE_PARM(wlan_reset, "s");
MODULE_PARM_DESC(wlan_reset, "Wlan reset polarity (high/low)");

#ifdef TI_BOARD_SETUP_PSC /*Sascha: Wird bei uns bereits in clocks.c definiert! */
void board_setup_psc(unsigned int domain, unsigned int id, char enable);
#endif

static unsigned char wlan_reset_val = 0;

//extern int davinci_i2c_write(u8 size, u8 * val, u16 client_addr);
//extern int davinci_i2c_read(u8 size, u8 * val, u16 client_addr);

/********************************************************************/
/*	Reset TNETW using the I2C controller							*/
/*																	*/
/********************************************************************/
int DaVinci_TNETW_Reset(int SetFlag)
{
    u8 val;
    volatile unsigned int temp;

    if (SetFlag==IN_RESET)
	{
//		printk("--Putting the TNET in Reset--\n");
//        val |= ( wlan_reset_val << WLAN_RESET_BIT );
		val = wlan_reset_val;
	}
	else
	{
//		printk("--Taking the TNET out of Reset--\n");
//        val |= ( (1-wlan_reset_val) << WLAN_RESET_BIT );
		val = 1 - wlan_reset_val;
	}
    printk("Write the value %x to the I2C mosulde\n",val);

    davinci_i2c_expander_op(0x3a, WLAN_RESET, val);
#if 0
    /* Configure U35 to mux the VLYNQ pins and take the TNET out of reset */
    printk("Put the TNET in reset and muxing the VLYNQ pins...\n");
    val = 0xfb;
//    davinci_i2c_write(1, &val, 0x3a);
	davinci_i2c_expander_op(0x3a, WLAN_RESET, val);

    /* Delay to make sure GPIOs are stable */
    for (temp=0; temp<=20000;temp++);

    /* Configure U35 to mux the VLYNQ pins and take the TNET out of reset */
    printk("Taking the TNET out of reset and muxing the VLYNQ pins...\n");

    val = 0xcb;
//    davinci_i2c_write(1, &val, 0x3a);
	davinci_i2c_expander_op(0x3a, WLAN_RESET, val);
#endif

    for (temp=0; temp<=20000;temp++);

	return 0;
}



/********************************************************************/
/****************V2U to TNETW1130 VLYNQ Setting**********************/
/********************************************************************/


/********************************************************************/
/*	arm_1130_vlynq_irq_cfg											*/
/*																	*/
/********************************************************************/
int arm_1130_vlynq_irq_cfg(void)
{
    IRQ_CONFIG_T    *p_irq    = &TNETW1x50_irq_config[0];

    /* Sets the Polarity of the Hardware IRQ Line */
    if(PAL_vlynqSetIrqPol(p_1x50_vlynq, p_irq->irq, pal_vlynq_high_irq_pol))
        return(-1);
        
	/* Sets the Type of the Hardware IRQ Line */
    if(PAL_vlynqSetIrqType(p_1x50_vlynq, p_irq->irq, pal_vlynq_level_irq_type))
        return(-1);

    return(0);
}

/********************************************************************/
/*	arm_1130_vlynq_re_setup											*/
/*																	*/
/********************************************************************/
int arm_1130_vlynq_re_setup(void)
{
    REGION_CONFIG_T    *p_region = &TNETW1x50_region_config[0];
    IRQ_CONFIG_T       *p_irq    = &TNETW1x50_irq_config[0];

    vlynq_delay_wait(0xfffff);
    
    /* Chack for a link to the remote device */
    if(!PAL_vlynqGetLinkStatus(p_1x50_vlynq))
    {
        printk("The Link to peer is down.\n");
        return (-1);
    }

    printk("Preparing the VLYNQ, now the Link is up. \n");

    /* Clears internal interrupt errors */
    if(PAL_vlynqIoctl(p_1x50_vlynq, 
                      PAL_VLYNQ_IOCTL_MAJOR_VAL(PAL_VLYNQ_IOCTL_CLEAR_INTERN_ERR), 0))
    {
        printk("Failed to clear the internal VLYNQ errors.\n");
        return (-1);
    }
    
    /* Setup, now the link is up */
    if(PAL_vlynqIoctl(p_1x50_vlynq,  
                      PAL_VLYNQ_IOCTL_MAJOR_VAL(PAL_VLYNQ_IOCTL_PREP_LINK_UP), 0))
    {
        printk("Failed to restore the VLYNQ. \n");
        return (-1);
    }

    while(p_region->id > -1)
    {
	    /* Maps one Memory Region of the Device */
        if(PAL_vlynqMapRegion(p_1x50_vlynq, p_region->remote, p_region->id,
				p_region->offset, p_region->size,
				p_vlynq_dev))
	{
            p_region->id = -1;
            printk("Failed to map the regions.\n");
            return (-1);	
	}

	p_region++;
    }
    while(p_irq->hw_intr_line > -1)
    {
	    /* Maps one IRQ Hardware Line Onto the VLYNQ */
        if(PAL_vlynqMapIrq(p_1x50_vlynq, p_irq->hw_intr_line, 
			     p_irq->irq, p_vlynq_dev))
	{
	    p_irq->hw_intr_line = -1;
            printk("Failed to map the irq(s).\n");
	    return(-1);
	}

        p_irq++;
    }
    
	/* Sets the polarity and the Type of the Hardware IRQ Line */
    if(arm_1130_vlynq_irq_cfg())
    {
        printk("Failed to set irq.\n");
        return (-1);
    }

    return(0);
}


/********************************************************************/
/*	arm_1130_vlynq_init												*/
/*																	*/
/********************************************************************/
PAL_Result arm_1130_vlynq_init(void)
{
    REGION_CONFIG_T *p_region = &TNETW1x50_region_config[0];
    IRQ_CONFIG_T *p_irq       = &TNETW1x50_irq_config[0];

    char dev_name[] = "wlan";
    int instance    = 0;

    printk("Start the 1350A vlynq initialization\n");

	/* Start VLYNQ configuration definition */
    vlynq_1x50_config.on_soc    = 	1; 				/* VLYNQ on host device (running the code) */
    vlynq_1x50_config.base_addr = 	ARM_VLYNQ_CONTROL_BASE;


     /* V1 port on V2U */
    vlynq_1x50_config.local_clock_dir    = pal_vlynq_clk_in;
    vlynq_1x50_config.local_clock_div    = VLYNQ_CLOCK_NO_DIV;
    vlynq_1x50_config.local_intr_local   = True;
    vlynq_1x50_config.local_intr_vector  = 31;
    vlynq_1x50_config.local_intr_enable  = True;
    vlynq_1x50_config.local_int2cfg      = True;
    vlynq_1x50_config.local_intr_pointer = 0x14; 
    vlynq_1x50_config.local_endianness   = pal_vlynq_little_en;
    vlynq_1x50_config.local_tx_addr      = 0; /*ARM_VLYNQ_MEM_MAP_BASE - DaVinci bug !!!!;*/
    vlynq_1x50_config.local_rtm_cfg_type 	 = no_rtm_cfg;
    vlynq_1x50_config.local_rtm_sample_value = 0; /* Do not care as rtm_cfg_type is no_rtm_cfg. */
    vlynq_1x50_config.local_tx_fast_path 	 = False;
   
	/*TNETW 1x50 */
    vlynq_1x50_config.peer_clock_dir     = pal_vlynq_clk_out;
    vlynq_1x50_config.peer_clock_div     = VLYNQ_CLOCK_NO_DIV;
    vlynq_1x50_config.peer_intr_local    = False;
    vlynq_1x50_config.peer_intr_vector   = 30;
    vlynq_1x50_config.peer_intr_enable   = True;
    vlynq_1x50_config.peer_int2cfg       = False;
    vlynq_1x50_config.peer_intr_pointer  = 0x14;    
    vlynq_1x50_config.peer_endianness    = pal_vlynq_little_en;
    vlynq_1x50_config.peer_tx_addr       = VLYNQ_TNETW1X50_VL0_PORTAL_OFFSET;
    vlynq_1x50_config.local_rtm_cfg_type 	 = no_rtm_cfg;
    vlynq_1x50_config.local_rtm_sample_value = 0; /* Do not care as rtm_cfg_type is no_rtm_cfg. */
    vlynq_1x50_config.local_tx_fast_path 	 = False;
	vlynq_1x50_config.init_swap_flag 		 = False;

	printk("Initializing the 1x50 chip\n");
	
	/* Creates a Device Reference */	
    p_1x50_vlynq = PAL_vlynqInit(&vlynq_1x50_config);
    if(!p_1x50_vlynq)
    {
        printk("Failed to initialize the vlynq @ 0x%08x.\n", vlynq_1x50_config.base_addr);
        printk("The error msg: %s.\n", vlynq_1x50_config.error_msg);
        goto av_vlynq_wlan_init_fail;
    }
    
	/* Save root VLYNQ in global variable for next VLYNQ hop */
	p_root_vlynq = p_1x50_vlynq;

	/* Creates a Device Reference */
    p_vlynq_dev = PAL_vlynqDevCreate(p_1x50_vlynq, dev_name, instance, 64, 1);
    if(!p_vlynq_dev)
    {
        printk("Failed to create the %s%d reference for vlynq.\n", 
                     dev_name, instance);
		goto av_vlynq_wlan_dev_fail;
    }
    
	/* Adds the Device Reference into the VLYNQ	*/
    if(PAL_vlynqAddDevice(p_1x50_vlynq, p_vlynq_dev, 1))
    {
        printk("Failed to add %s%d reference into vlynq.\n", 
                     dev_name, instance);
        goto av_vlynq_wlan_add_device_fail;
    }

    /* Load the memory regions mapping to the vlynq module registers */
    /* Loops until all the regilons are loaded */
    while(p_region->id > -1)
    {
	    /* Maps one Memory Region of the Device */	    
        if(PAL_vlynqMapRegion(p_1x50_vlynq, p_region->remote, p_region->id,
				p_region->offset, p_region->size,
				p_vlynq_dev))
		{
        	p_region->id = -1;
        	printk("Failed to map regions for %s%d in vlynq.\n", dev_name, instance);
        	goto av_vlynq_wlan_map_region_fail;	
		}

		p_region++;
    }

    /* Load the interrupt mapping to the vlynq module */
    while(p_irq->hw_intr_line > (signed char)(-1))
    {
	    /* Maps one IRQ Hardware Line Onto the VLYNQ */
        if(PAL_vlynqMapIrq(p_1x50_vlynq, p_irq->hw_intr_line, 
			     p_irq->irq, p_vlynq_dev))
		{
			p_irq->hw_intr_line = -1;
			printk("Failed to map interrupts for %s%d in vlynq.\n", dev_name, instance);
			goto av_vlynq_wlan_map_irq_fail;
		}

    	p_irq++;
    }
    
	/* Sets the polarity and the Type of the Hardware IRQ Line */
    if(arm_1130_vlynq_irq_cfg()) 
	{
        printk("Failed to set the irq on peer vlynq.\n");
        goto av_vlynq_wlan_set_irq_fail;      
	}


    printk("Success in setting up the 1350 VLYNQ.\n");

    reset_flag = 0;

    return PAL_VLYNQ_OK;

/* Error handling code */
av_vlynq_wlan_set_irq_fail:
av_vlynq_wlan_map_irq_fail:

    printk("Unmapping the VLYNQ IRQ(s).\n");
    p_irq = &TNETW1x50_irq_config[0];
    while(p_irq->hw_intr_line > -1)
    {
        PAL_vlynqUnMapIrq(p_1x50_vlynq, p_irq->irq, p_vlynq_dev);
        p_irq++;
    }


av_vlynq_wlan_map_region_fail:

    printk("Un mapping the VLYNQ regions.\n");
    p_region = &TNETW1x50_region_config[0];
    while(p_region->id > -1)
    {
        PAL_vlynqUnMapRegion(p_1x50_vlynq, p_region->remote, p_region->id,
			     p_vlynq_dev);
 
        p_region++;
    }

    PAL_vlynqRemoveDevice(p_1x50_vlynq, p_vlynq_dev);

av_vlynq_wlan_add_device_fail:

    printk("Destroying the VLYNQ device.\n");
    PAL_vlynqDevDestroy(p_vlynq_dev);

av_vlynq_wlan_dev_fail:

    printk("Cleaning up.......\n");
    if(PAL_vlynqCleanUp(p_1x50_vlynq))
        printk("It is not a Clean shut down...\n");

av_vlynq_wlan_init_fail:
    printk("End.......\n");
	return PAL_VLYNQ_INTERNAL_ERROR;
}



/********************************************************************/
/****************Main vlynq chain initializtion**********************/
/********************************************************************/
void vlynq_dev_init(void)
{
	PAL_Result FunctionResult;
	
    /* Start VLYNQ initialization */
    PAL_sysVlynqInit();

	FunctionResult=arm_1130_vlynq_init();
	if (FunctionResult==PAL_VLYNQ_OK)
	{
        printk("OK.....\n");
        printk("ARM_SDRAM_BASE = %x\n",ARM_SDRAM_BASE);

	    /* Get physical addresses for 1130 */ 
	    if(PAL_vlynqGetDevBase(p_1x50_vlynq, VLYNQ_TNETW1X50_MEM_OFFSET, &wlan_mem_addr, p_vlynq_dev))
	        printk("Not there yet. Still problems.\n");

		wlan_mem_addr = wlan_mem_addr + 0x0c000000;
		
	    //wlan_mem_addr = (unsigned int)ioremap(wlan_mem_addr, VLYNQ_TNETW1X50_MEM_SIZE);
	        
	    printk("the WLAN memory is mapped at address %x \n", wlan_mem_addr);

	    if(PAL_vlynqGetDevBase(p_1x50_vlynq, VLYNQ_TNETW1X50_REG_OFFSET, &wlan_reg_addr, p_vlynq_dev))
			printk("Still problematic. \n");
			
		wlan_reg_addr = wlan_reg_addr + 0x0c000000;
				
	    //wlan_reg_addr = (unsigned int)ioremap(wlan_reg_addr, VLYNQ_TNETW1X50_REG_SIZE);
				
		printk("the WLAN registers area is mapped at address %x \n", wlan_reg_addr);
	}
	else
	{
		printk("\n==>>Function %s: arm_1130_vlynq_init failed with code %d!\n",__FUNCTION__,FunctionResult);
	}
}



void DaVinci_evm_vlynq_pins_config( void )
{
    volatile unsigned int temp;

#if 1
    printk("changing GPIO19 direction form: %x \n",*DAVINCI_GPIO_DIR_REG); 
    *DAVINCI_GPIO_DIR_REG &= ~(0x00080000); /* GPIO19 to output mode */
    printk("To : %x\n",*DAVINCI_GPIO_DIR_REG);

    printk("changing GPIO19 output level from: %x \n",*DAVINCI_GPIO_OUT_DATA); 
    *DAVINCI_GPIO_OUT_DATA &= ~(0x00080000); /* Zero on GPIO19 */
    printk("To : %x\n",*DAVINCI_GPIO_OUT_DATA);
#endif
    
    printk("Lighting some LED's...\n");
//    val = 0x55;
//    davinci_i2c_write(1, &val, 0x38);
//	davinci_i2c_expander_op(0x3a, WLAN_RESET, val);

	/* Configure PinMux0 to use the vlynq pins and GPIOs 10-21 */
    printk("Muxing the VLYNQ pins using PinMux0...\n");
    
	/* Mask bits 0-15 in PinMux0 register */
    *PINMUX0 &= ~0x0000ffff;

    /* Program the PinMux0 register */
    *PINMUX0 |= 0x0000d00b;/*d00b*/

  	printk("Mux: PinMux0=%x\n",*PINMUX0);

    /* Delay to make sure GPIOs are stable */
    for (temp=0; temp<=20000;temp++);

	davinci_i2c_expander_op(0x3a, VLYNQ_ON, 0);

    DaVinci_TNETW_Reset(OUT_OF_RESET);

    DaVinci_TNETW_Reset(IN_RESET);

  	printk("VLYNQ: Version Register=%x\nVLYNQ: Control Register=%x \nVLYNQ: Status Register=%x\n",
  			*VLYNQ_VER, *VLYNQ_CTRL, *VLYNQ_STAT);

}

#ifdef TI_BOARD_SETUP_PSC
void board_setup_psc(unsigned int domain, unsigned int id, char enable)
{
        volatile unsigned int *mdstat = (unsigned int *)((int)MDSTAT + 4 * id);
        volatile unsigned int *mdctl = (unsigned int *)((int)MDCTL + 4 * id);
        
	if (enable) {
		*mdctl |= 0x00000003;
	} else {
		*mdctl &= 0xFFFFFFF2;
	}
                                                                                
        if ((PDSTAT & 0x00000001) == 0) {
                PDCTL1 |= 0x1;
                PTCMD = (1 << domain);
                while ((((EPCPR >> domain) & 1) == 0)) ;
                                                                                
                PDCTL1 |= 0x100;
                while (!(((PTSTAT >> domain) & 1) == 0)) ;
        } else {
                PTCMD = (1 << domain);
                while (!(((PTSTAT >> domain) & 1) == 0)) ;
        }
        
	if (enable) {                                                                        	while (!((*mdstat & 0x0000001F) == 0x3)) ;
	} else {
        	while (!((*mdstat & 0x0000001F) == 0x2)) ;
	}
}
#endif

/********************************************************************/
/*	Configure the DM320 on the DaVinci board to activate the VLYNQ */
/********************************************************************/
void Davinci_Vlynq_Hardware_Init( void )
{

	/* Turn on the VLYNQ PSC */
	board_setup_psc(DAVINCI_GPSC_ARMDOMAIN, DAVINCI_LPSC_VLYNQ, 1);
	/* Turn on the GPIO module */
	board_setup_psc(DAVINCI_GPSC_ARMDOMAIN, DAVINCI_LPSC_GPIO, 1);

    /* We are getting the wlan reset polarity from Bootloader */
    if (strcmp(wlan_reset, "low") == 0) 
    {
        printk("Wlan reset polarity is high (Beta board)\n");
        wlan_reset_val = 0;
    } 
    else 
    {
        printk("Wlan reset polarity is low (Gamma board)\n");
        wlan_reset_val = 1;
    }
	/* Configure the DaVinci EVM to use the VLYNQ pins */
	DaVinci_evm_vlynq_pins_config();
	
    /* Start the vlynq chain initialization */	
	vlynq_dev_init();
}
EXPORT_SYMBOL(arch_getTnetResources);
#if 0
EXPORT_SYMBOL(arch_cacheFlushRange);
EXPORT_SYMBOL(arch_cacheInvalidateRange);

/*-----------------------------------------------------------------------------

Routine Name:

        arch_cacheFlushRange

Routine Description:

	Clean (write back) the specified virtual address range. 
	
Arguments:
	startAddr  - virtual start address
	size       - length in bytes

Return Value:

        None

-----------------------------------------------------------------------------*/

void
arch_cacheFlushRange(void* startAddr, size_t size)
{
	dmac_flush_range((int)startAddr, (int)startAddr + size);
}

/*-----------------------------------------------------------------------------

Routine Name:

        arch_cacheInvalidateRange

Routine Description:

	Invalidate (discard) the specified virtual address range.
	
Arguments:
	startAddr  - virtual start address
	size       - length in bytes

Return Value:

        None

-----------------------------------------------------------------------------*/
void
arch_cacheInvalidateRange(void* startAddr, size_t size)
{
    dmac_inv_range((int)startAddr, (int)startAddr + size);
}
#endif

/*-----------------------------------------------------------------------------

Routine Name:

        arch_getTnetResources(

Routine Description:

	return the memory and register WLAN address.after coonversion of the VLYNQ interface
	
Arguments:
	tnet_mem_addr  - pointer to the virtual WLAN memory start address
	net_reg_addr      - pointer to the virtual WLAN registers start address


Return Value:

        None

-----------------------------------------------------------------------------*/
void 
arch_getTnetResources(unsigned int* tnet_mem_addr, unsigned int* tnet_reg_addr)
{
	*tnet_mem_addr = wlan_mem_addr;     	/* WLAN memory base */
	*tnet_reg_addr = wlan_reg_addr;     	/* WLAN register area base */
}


