/*-------------------------------------------------------------------------------------*\
 *
 *  $Id: gpio_ssi.c 1.10 2004/03/04 15:03:19Z strevisany Exp $
 *
 *  $Log: gpio_ssi.c $
 *  Revision 1.10  2004/03/04 15:03:19Z  strevisany
 *  Revision 1.9  2004/03/03 15:56:54Z  strevisany
 *  Revision 1.8  2004/02/12 09:54:26Z  strevisany
 *  Revision 1.7  2004/02/11 12:05:25Z  mpommerenke
 *  Revision 1.6  2004/02/11 11:49:00Z  strevisany
 *  Revision 1.5  2004/02/10 16:16:33Z  strevisany
 *  Revision 1.4  2004/02/10 14:48:09Z  strevisany
 *  Revision 1.3  2004/02/10 13:32:30Z  strevisany
 *  Revision 1.2  2004/02/10 12:00:04Z  mpommerenke
 *  Revision 1.1  2003/11/11 10:32:37Z  mpommerenke
 *
\*-------------------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
/*--- #include <asm/avalanche/ledapp.h> ---*/
#include <linux/module.h>
/*--- #include <asm/gpio.h> ---*/
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/semaphore.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/avalanche/avalanche_map.h>
#include <asm/avalanche/generic/avalanche_misc.h>
#include <linux/module.h>

#define TRUE    1
#define FALSE   0
/*-------------------------------------------------------------------------------------*\
\*-------------------------------------------------------------------------------------*/
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 unsigned long 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 ---*/
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CLK,  GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);
	sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
    if ((avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == FALSE) && (avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CS) != FALSE)) { /*--- kein Kurzschluß ---*/
		avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS, GPIO_PIN, GPIO_INPUT_PIN);
		sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
        if ((avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == FALSE) && (avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CS) == FALSE)) { /*--- 1 KOhm R vorhanden ? ---*/
			/*--- CS low,DCLK high ---*/
		    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS, FALSE);
		    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);
            sleep(); /*--- PIO Port zeit geben zum umschalten(ca. xx us) ---*/
            if ((avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CLK) == TRUE) && (avalanche_gpio_in_bit(CONFIG_GPIO_SSI_CS) == TRUE)) { /*--- 1 KOhm R sicher vorhanden ---*/
				DataOnly = TRUE;
            }
        }
    	avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS, GPIO_PIN, GPIO_OUTPUT_PIN);
    }
    else printk("ERROR: gpio_dataonly_test short circuit!!!\n");
    /* CS,CLK ruecksetzen  */
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avalanche_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;
	spin_lock_irqsave(&gpio_device_lock, flags);
    /*--- printk("gpio_ssi_init: init\n"); ---*/
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS3,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS3, TRUE);
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS2,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS2, TRUE);
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CS,   GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS, TRUE);
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_CLK,  GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);
    avalanche_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_OUTPUT_PIN);
    avalanche_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)
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS, select ? FALSE : TRUE);
    if (chip & 0x02)
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS2, select ? FALSE : TRUE);
    if (chip & 0x04) {
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CS3, select ? FALSE : TRUE);
        /* DECT: "busy waiting" durch Dummy-Schreibzugriffe */
        if (data_is_input == TRUE)
        {
            avalanche_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--)  {
            avalanche_gpio_out_bit(CONFIG_GPIO_SSI_DATA, TRUE);  /* Dummy-Schreibzugriff */
            avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);   /* 1 Zyklus etwa 750 ns */
            avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);  
        }
    }

    if ((data_is_input != FALSE) && (select == FALSE)) {	/* am schluss wieder auf output */
    	avalanche_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) {
		avalanche_gpio_ctrl(CONFIG_GPIO_SSI_DATA, GPIO_PIN, GPIO_INPUT_PIN);
		data_is_input = TRUE;
	}
    for (index = 128; index > 0; index = index>>1)  
    {
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit rein mit CLK auf 0 */
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE); /* Datenbit rein mit CLK auf 1 */
        if (avalanche_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) {
		avalanche_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 {
                        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, TRUE);   /* Zyklusdauer etwa 1us  */
                        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);  /* auslesen bei CLK auf 0 */
                    } while (avalanche_gpio_in_bit(CONFIG_GPIO_SSI_DATA) && wait_cycles--);
                    index = (index >> 1);    /* das oberste Bit ist die bereits gelesen '0' */
                    avalanche_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);
                }
                avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit rein mit CLK auf 0 */
                if (avalanche_gpio_in_bit(CONFIG_GPIO_SSI_DATA)) {
                    *in |= index;
                }
                avalanche_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)  
    {
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_DATA, ((byte & (unsigned char)index) == 0) ? FALSE : TRUE);/* Datenbit auf 0/1 */
        avalanche_gpio_out_bit(CONFIG_GPIO_SSI_CLK, FALSE);/* Datenbit raus mit CLK auf 0 */
        avalanche_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
