/*-----------------------------------------------------------------------------------------------*\
\*-----------------------------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <asm/fcntl.h>
#include <asm/ioctl.h>
#include <asm/errno.h>
#include <asm/uaccess.h>

#include <asm/mach_avm.h>
#include <asm/mips-boards/ohio.h>
#include <asm/mach-ohio/hw_gpio.h>
#include <asm/mach-ohio/hw_reset.h>

#define DECT_TAC_CLOCKCYCLES 44     /* 1 Zyklus etwa 750ns */
#define DECT_TLN_CLOCKCYCLES 280    /* 1 Zyklus etwa 750ns */

/*------------------------------------------------------------------------------------------*\
\*------------------------------------------------------------------------------------------*/
#if defined(CONFIG_GPIO_SSI)
#include <asm/mips-boards/ohio_ssi.h>
#define TRUE  1
#define FALSE 0

static struct resource ssi_gpioressource[] = {
{
    .name  = "ssi_cs",
    .flags = IORESOURCE_IO,		 
    .start = CONFIG_GPIO_SSI_CS,
    .end   = CONFIG_GPIO_SSI_CS
},
{
    .name  = "ssi_cs2",
    .flags = IORESOURCE_IO,		 
    .start = CONFIG_GPIO_SSI_CS2,
    .end   = CONFIG_GPIO_SSI_CS2
},
{
    .name  = "ssi_cs3",
    .flags = IORESOURCE_IO,		 
    .start = CONFIG_GPIO_SSI_CS3,
    .end   = CONFIG_GPIO_SSI_CS3
},
{
    .name  = "ssi_clk",
    .flags = IORESOURCE_IO,		 
    .start = CONFIG_GPIO_SSI_CLK,
    .end   = CONFIG_GPIO_SSI_CLK
},
{
    .name  = "ssi_data",
    .flags = IORESOURCE_IO,		 
    .start = CONFIG_GPIO_SSI_DATA,
    .end   = CONFIG_GPIO_SSI_DATA
}
};
unsigned int gRequestSSI_ok;
/*--------------------------------------------------------------------------------*\
\*--------------------------------------------------------------------------------*/
static int ssi_requestinit(void) {
    int i;
    if(gRequestSSI_ok == 0) {
        for(i = 0; i < sizeof(ssi_gpioressource) / sizeof(struct resource); i++) {
            if(request_resource(&gpio_resource, &ssi_gpioressource[i])) {
                printk(KERN_ERR"[ssi_init]ERROR: ssi-resource %d gpio %ld-%ld not available\n", i, ssi_gpioressource[i].start, ssi_gpioressource[i].end);  
                /*--- return 1; ---*/
            }
        }
        gRequestSSI_ok = 1;
    }
    return 0;
}
#if 0
/*--------------------------------------------------------------------------------*\
\*--------------------------------------------------------------------------------*/
static void ssi_requestrelease(void) {
    unsigned int i;
    if(gRequestSSI_ok == 1) {
        for(i = 0; i < sizeof(ssi_gpioressource) / sizeof(struct resource); i++) {
            release_resource(&ssi_gpioressource[i]);
        }
        gRequestSSI_ok = 0;
    }
}
#endif
/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
extern spinlock_t	gpio_device_lock;
static int data_is_input;
/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
static struct timer_list ssi_timmer;
static struct semaphore ssi_sema;
static void Ssi_timer_Handler(unsigned long nr) {
    del_timer(&ssi_timmer);
	up(&ssi_sema);
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
static void sleep(void) {

    sema_init(&ssi_sema, 0);
	init_timer(&ssi_timmer);
	ssi_timmer.data = 0;
	ssi_timmer.function = Ssi_timer_Handler;
    ssi_timmer.expires = jiffies + (2* (HZ / 100)); /*--- 10-20 ms ---*/
    add_timer(&ssi_timmer);
	down_interruptible(&ssi_sema);
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
int gpio_dataonly_test(void) {
	int DataOnly = 0;
	unsigned int flags = 0x00;
	spin_lock_irqsave(&gpio_device_lock, flags);
    /* Testen ob Sicofi/DUSLIC da ist */
    /*--- CS high, DCLK low ---*/
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CS,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CLK,  GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);
	sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
    if ((avm_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == FALSE) && (avm_gpio_in_bit(CONFIG_GPIO_SSI_CS) != FALSE)) { /*--- kein Kurzschluß ---*/
		avm_gpio_ctrl(CONFIG_GPIO_SSI_CS, GPIO_PIN, GPIO_INPUT_PIN);
		sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
        if ((avm_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == FALSE) && (avm_gpio_in_bit(CONFIG_GPIO_SSI_CS) == FALSE)) { /*--- 1 KOhm R vorhanden ? ---*/
			/*--- CS low,DCLK high ---*/
		    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS, FALSE);
		    avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);
            sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
            if ((avm_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == TRUE) && (avm_gpio_in_bit(CONFIG_GPIO_SSI_CS) == TRUE)) { /*--- 1 KOhm R sicher vorhanden ---*/
				DataOnly = TRUE;
            }
        }
    	avm_gpio_ctrl(CONFIG_GPIO_SSI_CS, GPIO_PIN, GPIO_OUTPUT_PIN);
    }
    else printk("ERROR: gpio_dataonly_test short circuit!!!\n");
    /* CS,CLK ruecksetzen  */
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);
    printk("gpio_dataonly_test: %d\n",DataOnly);
	spin_unlock_irqrestore(&gpio_device_lock, flags);
    return DataOnly;
}
/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
void gpio_ssi_init(void) {
	unsigned int flags = 0x00;

    ssi_requestinit();
	spin_lock_irqsave(&gpio_device_lock, flags);
    /*--- printk("gpio_ssi_init: init\n"); ---*/
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CS3,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS3, TRUE);
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CS2,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS2, TRUE);
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CS,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avm_gpio_ctrl(CONFIG_GPIO_SSI_CLK,  GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);
    avm_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_OUTPUT_PIN);
    avm_gpio_out_bit(CONFIG_GPIO_SSI_DATA, TRUE);
	data_is_input = FALSE;
    printk("gpio_ssi_init: done\n");
	spin_unlock_irqrestore(&gpio_device_lock, flags);
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
void gpio_ssi_select(unsigned int chip, unsigned int select) {
    unsigned int flags = 0x00;
    unsigned int cnt;
    /*--- printk("gpio_ssi_select: chip %x= %d\n", chip, select); ---*/
    spin_lock_irqsave(&gpio_device_lock, flags);
    if (chip & 0x01)
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CS, select ? FALSE : TRUE);
    if (chip & 0x02)
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CS2, select ? FALSE : TRUE);
    if (chip & 0x04) {
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CS3, select ? FALSE : TRUE);
        /* DECT: "busy waiting" durch Dummy-Schreibzugriffe */
        if (data_is_input == TRUE)
        {
            avm_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_OUTPUT_PIN);
            data_is_input = FALSE;
        }
        for (cnt = (select ? DECT_TAC_CLOCKCYCLES : DECT_TLN_CLOCKCYCLES); cnt > 0; cnt--)  {
            avm_gpio_out_bit(CONFIG_GPIO_SSI_DATA, TRUE);  /* Dummy-Schreibzugriff */
            avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);   /* 1 Zyklus etwa 750 ns */
            avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);  
        }
    }

    if ((data_is_input != FALSE) && (select == FALSE)) {	/* am schluss wieder auf output */
    	avm_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_OUTPUT_PIN);
		data_is_input = FALSE;
    }
    spin_unlock_irqrestore(&gpio_device_lock, flags);
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
unsigned int gpio_ssi_read(void) {
    unsigned int flags = 0x00;
    unsigned int index;
    unsigned int in = 0;
    /*--- printk("gpio_ssi_read: start\n"); ---*/

    spin_lock_irqsave(&gpio_device_lock, flags);
	if (data_is_input == FALSE) {
		avm_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_INPUT_PIN);
		data_is_input = TRUE;
	}
    for (index = 128; index > 0; index = index>>1)  
    {
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit rein mit CLK auf 0 */
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE); /* Datenbit rein mit CLK auf 1 */
        if (avm_gpio_in_bit(CONFIG_GPIO_SSI_DATA)) {
            in |= index;
        }
    }
    spin_unlock_irqrestore(&gpio_device_lock, flags);
    /*--- printk("gpio_ssi_read: %x\n", in); ---*/
    return in;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
unsigned int gpio_ssi_detect0_and_read(unsigned char* buffer) {
    unsigned int   flags = 0x00;
    unsigned int   index;
    unsigned char* in = buffer;
    unsigned int   frame_cnt;
    unsigned int   byte_cnt;
    unsigned int   wait_cycles = 500;    /* Vermeidung einer Endlosschleife */

    /* auf 'Input' schalten */
    /*--- printk("gpio_ssi_detect0_and_read: start\n"); ---*/
	if (data_is_input == FALSE) {
		avm_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_INPUT_PIN);
		data_is_input = TRUE;
	}

    /* 11 Frames zu jeweils 4 Octets einlesen (erster Frame enthält Handshake) */
    spin_lock_irqsave(&gpio_device_lock, flags);
    for (frame_cnt = 0; frame_cnt < 11; frame_cnt++)
    {
        for (byte_cnt = 0; byte_cnt < 4; byte_cnt++, in++)
        {
            *in = 0;
            for (index = (1 << 7); index > 0; index = (index >> 1))  
            {
                /* vor dem ersten Frame: warten auf das erste '0'-bit */
                if (frame_cnt == 0 && byte_cnt == 0 && index == (1 << 7))
                {
                    do {
                        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);   /* Zyklusdauer etwa 1us  */
                        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);  /* auslesen bei CLK auf 0 */
                    } while (avm_gpio_in_bit(CONFIG_GPIO_SSI_DATA) && wait_cycles--);
                    index = (index >> 1);    /* das oberste Bit ist die bereits gelesen '0' */
                    avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE); /* CLK auf TRUE am Ende des Zyklus */
                }

                if (byte_cnt == 3 && index == 1)
                {
                    /* vor der letzten negative Flanke des Frames: Interrupts sperren */
                    spin_lock_irqsave(&gpio_device_lock, flags);
                }
                avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit rein mit CLK auf 0 */
                if (avm_gpio_in_bit(CONFIG_GPIO_SSI_DATA)) {
                    *in |= index;
                }
                avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE); 
                if (byte_cnt == 0 && index == (1 << 6))
                {
                    /* am Ende des 2. eingelesenen Bits nach positiver Flanke Interrupts freigeben */
                    spin_unlock_irqrestore(&gpio_device_lock, flags);
                }
            }
        }
        /* Handshake-Frame auswerten: falls nicht RECV_CONF (=0x0F), dann raus */
        if (frame_cnt == 0 && buffer[0] != 0x0F)
        {
            spin_unlock_irqrestore(&gpio_device_lock, flags);
            return 4;
        }
    }
    spin_unlock_irqrestore(&gpio_device_lock, flags);
    /*--- printk("gpio_ssi_read: %x\n", in); ---*/
    return 44;
}

/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
void gpio_ssi_write(unsigned int byte) {
    unsigned int flags = 0x00;
    unsigned int index;
    /*--- printk("gpio_ssi_write: %x\n", byte); ---*/

    spin_lock_irqsave(&gpio_device_lock, flags);
    for (index = 128; index > 0; index = index>>1)  
    {
        avm_gpio_out_bit(CONFIG_GPIO_SSI_DATA, ((byte & (unsigned char)index) == 0) ? FALSE : TRUE);/* Datenbit auf 0/1 */
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit raus mit CLK auf 0 */
        avm_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE); /* Datenbit raus mit CLK auf 1 */
    }
    spin_unlock_irqrestore(&gpio_device_lock, flags);
}

EXPORT_SYMBOL(gpio_dataonly_test);
EXPORT_SYMBOL(gpio_ssi_init);
EXPORT_SYMBOL(gpio_ssi_read);
EXPORT_SYMBOL(gpio_ssi_detect0_and_read);
EXPORT_SYMBOL(gpio_ssi_write);
EXPORT_SYMBOL(gpio_ssi_select);

#endif
