/*
 * Two-Wire Serial Interface (TWSI) support for the GT64260
 */

/*
 * Copyright (c) 2004, Brookhaven National Laboratory and
 *                 Shuchen Kate Feng <feng1@bnl.gov>
 * All rights reserved.
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution.
 *
 * See section 24:TWSI interface of "the GT-64260B System Controller
 * for powerPc Processors Data Sheet".
 *
 * For full TWSI protocol description look in Philips Semiconductor
 * TWSI spec.
 *
 * We need it to read out I2C devices used for the MVME5500
 * (eg. the memory SPD and VPD).
 */

#include <libcpu/spr.h>  /*registers.h included here for rtems_bsp_delay()*/
#include <libcpu/io.h>
#include <rtems/bspIo.h>

#include "bsp/gtreg.h"
#include "bsp/GT64260TWSI.h"

#define MAX_LOOP 100

#define TWSI_DEBUG 0

static int TWSI_initFlg = 0;  /* TWSI Initialization Flag */

void GT64260TWSIinit(void)
{
  if ( !TWSI_initFlg ) {
#if TWSI_DEBUG
   printk("GT64260TWSIinit(");
#endif
   outl( 0, TWSI_SFT_RST); /* soft reset */
   rtems_bsp_delay(1000);

   /* See 24.2.5 : Assume bus speed is 133MHZ
    * Try to be close to the default frequency : 62.5KHZ
    * value 0x2c: 69.27 KHz TWSI bus clock
    */
   outl(0x2c, TWSI_BAUDE_RATE);
   rtems_bsp_delay(1000);

   /* Set Acknowledge and enable TWSI in the Control register */
   outl(0x44, TWSI_CTRL);
   rtems_bsp_delay(4000);
   TWSI_initFlg = 1;
#if TWSI_DEBUG
   printk(")\n");
#endif
  }
}

/* return the interrupt flag */
static int GT64260TWSIintFlag(void)
{
  unsigned int loop;

  for (loop = 0; loop < MAX_LOOP; loop++ ) {
    /* Return 1 if the interrupt flag is set */
    if (inl(TWSI_CTRL) & TWSI_INTFLG)
      return(1);
    rtems_bsp_delay(1000);
  }
  return(0);
}

int GT64260TWSIstop(void)
{

#if TWSI_DEBUG
  printk("GT64260TWSIstop(");
#endif

  outl((inl(TWSI_CTRL) | TWSI_STOP), TWSI_CTRL);
  rtems_bsp_delay(1000);

  /* Check if interrupt flag bit is set*/
  if (GT64260TWSIintFlag()) {
     outl((inl( TWSI_CTRL) & ~TWSI_INTFLG), TWSI_CTRL);
     rtems_bsp_delay(1000);
#if TWSI_DEBUG
     printk(")\n");
#endif
     return(0);
  }
#if TWSI_DEBUG
     printk("NoIntFlag\n");
#endif
  return(-1);
}

int GT64260TWSIstart(void)
{
  unsigned int loop;
  unsigned int status;

#if TWSI_DEBUG
  printk("GT64260TWSIstart(");
#endif
  /* Initialize the TWSI interface */
  GT64260TWSIinit();

  /* set the start bit */
  outl((TWSI_START | TWSI_TWSIEN), TWSI_CTRL);
  rtems_bsp_delay(1000);

  if (GT64260TWSIintFlag()) {
    /* Check for completion of START sequence */
    for (loop = 0; loop<MAX_LOOP; loop++ ) {
      /* if (start condition transmitted) ||
       *    (repeated start condition transmitted )
       */
      if (((status= inl( TWSI_STATUS)) == 8) || (status == 0x10)) {
#if TWSI_DEBUG
        printk(")");
#endif
        return(0);
      }
      rtems_bsp_delay(1000);
    }
  }
  /* if loop ends or intFlag ==0 */
  GT64260TWSIstop();
  return(-1);
}

int GT64260TWSIread(unsigned char * pData, int lastByte)
{
  unsigned int loop;

#if TWSI_DEBUG
  printk("GT64260TWSIread(");
#endif
  /* Clear INTFLG and set ACK and ENABLE bits */
  outl((TWSI_ACK | TWSI_TWSIEN), TWSI_CTRL);
  rtems_bsp_delay(1000);

  if (GT64260TWSIintFlag()) {
    for (loop = 0; loop< MAX_LOOP; loop++) {
      /* if Master received read data, acknowledge transmitted */
      if ( (inl( TWSI_STATUS) == 0x50)) {
        *pData = (unsigned char) inl( TWSI_DATA);
        rtems_bsp_delay(1500);

        /* Clear INTFLAG and set Enable bit only */
        if (lastByte)
         outl(TWSI_TWSIEN, TWSI_CTRL);
        rtems_bsp_delay(1500);
#if TWSI_DEBUG
        printk(")\n");
#endif
        return(0);
      }
      rtems_bsp_delay(1000);
    } /* end for */
  }
  /* if loop ends or intFlag ==0 */
  GT64260TWSIstop();
  return(-1);
}

/* do a TWSI write cycle on the TWSI bus*/
int GT64260TWSIwrite(unsigned char Data)
{
  unsigned int loop;
  unsigned int status;

#if TWSI_DEBUG
  printk("GT64260TWSIwrite(");
#endif
  /* Write data into the TWSI data register */
  outl(((unsigned int) Data), TWSI_DATA);
  rtems_bsp_delay(1000);

  /* Clear INTFLG in the control register to drive data onto TWSI bus */
  outl(0, TWSI_CTRL);
  rtems_bsp_delay(1000);

  if (GT64260TWSIintFlag() ) {
    for (loop = 0; loop< MAX_LOOP; loop++) {
      rtems_bsp_delay(1000);
      /* if address + write bit transmitted, acknowledge not received */
      if ( (status = inl( TWSI_STATUS)) == 0x20) {
        /* No device responding, generate STOP and return -1 */
        printk("no device responding\n");
        GT64260TWSIstop();
        return(-1);
      }
      /* if (address + write bit transmitted, acknowledge received)
       * (Master transmmitted data byte, acknowledge received)
       *    (address + read bit transmitted, acknowledge received)
       */
      if ((status == 0x18)||(status == 0x28)||(status == 0x40)) {
#if TWSI_DEBUG
        printk(")\n");
#endif
        return(0);
      }
      rtems_bsp_delay(1000);
    } /* end for */
  }
  printk("No correct status, timeout\n");
  GT64260TWSIstop();
  return(-1);
}
