#include <stdio.h>
#include "cavium.h"
#include "peekpoke.h"
#include "dioctl.h"
#include "dioname.h"

extern int BusCyclesLog;
#ifndef CLIENT_ONLY
void *base;
volatile unsigned *pa,*pb,*pc;
volatile unsigned short *syscon;

void ts4200_SetDioDir(int dionum,int asOutput);
void ts4200_SetDio(int dionum,int asHigh);
int ts4200_GetDioDir(int dionum);
int ts4200_GetDio(int dionum);


void ts4200_DioDataInit() {
}

void ts4200_CommitDio() {
}

void ts4200_DioInit() {
  int i,devmem=-1;

  SetDioDir = ts4200_SetDioDir;
  SetDio = ts4200_SetDio;
  GetDioDir = ts4200_GetDioDir;
  GetDio = ts4200_GetDio;
  CommitDio = ts4200_CommitDio;


  base = map_phys(0xfffff400,&devmem);
  pa = base;
  pb = base + 0x200;
  pc = base + 0x400;
  syscon = map_phys(0x30000000,&devmem);
  ts4200_DioDataInit();
}

void ts4200_SetDioDir(int dionum,int asOutput) {
  if (BusCyclesLog & 4) {
    fprintf(stderr,"ts4200_SetDioDir(%d,%d)\n",dionum,asOutput);
  }
  if (dionum < 0 || dionum >= 161 || DioCapabilities[dionum] == DIO_NOT_PRESENT) return;
  if (dionum < 32) { // PA
    pa[0] = (1 << dionum); // enable PIO mode
    pa[asOutput ? 0x10/4 : 0x14/4] = (1 << dionum); // enable/disable output
  } else if (dionum < 64) { // PB
    if (dionum != 63) { // 63==PB31 access prohibited since it control FPGA clk
      pb[0] = (1 << (dionum-32)); // enable PIO mode
      pb[asOutput ? 0x10/4 : 0x14/4] = (1 << (dionum-32)); // enable/disable output
    }
  } else if (dionum < 96) { // PC
    pc[0] = (1 << (dionum-64)); // enable PIO mode
    pc[asOutput ? 0x10/4 : 0x14/4] = (1 << (dionum-64)); // enable/disable output
  } else if (dionum < 112) { // FPGA
    if (asOutput) {
      //syscon[4/2] |= (1 << (dionum-96));
      syscon[4/2] = syscon[4/2] | (1 << (dionum-96));
    } else {
      syscon[4/2] &= ~(1 << (dionum-96));
    }
  } else if (dionum < 128) {
    if (asOutput) {
      syscon[0x12/2] = syscon[0x12/2] | (1 << (dionum-112));
    } else {
      syscon[0x12/2] &= ~(1 << (dionum-112));
    }
  } else if (dionum < 130) {
  } else if (dionum <= 146) {
    if (asOutput) {
      syscon[0x408/2] |= (1 << (dionum-130));
    } else {
      syscon[0x408/2] &= ~(1 << (dionum-130));
    }
  } else if (dionum <= 161) {
    if (asOutput) {
      syscon[0x40A/2] |= (1 << (dionum-146));
    } else {
      syscon[0x40A/2] &= ~(1 << (dionum-146));
    }
  }
}

void ts4200_SetDio(int dionum,int asHigh) {
  if (BusCyclesLog & 4) {
    fprintf(stderr,"ts4200_SetDio(%d,%d)\n",dionum,asHigh);
  }
  if (dionum < 0 || dionum >= 130 || DioCapabilities[dionum] == DIO_NOT_PRESENT) return;
  if (dionum < 32) { // PA
    pa[asHigh?0x30/4:0x34/4] = (1 << dionum); // set/clear output data
  } else if (dionum < 64) { // PB
    if (dionum != 63) { // 63==PB31 access prohibited since it control FPGA clk
      pb[asHigh?0x30/4:0x34/4] = (1 << (dionum-32)); // set/clear output data
    }
  } else if (dionum < 96) { // PC
    pc[asHigh?0x30/4:0x34/4] = (1 << (dionum-64)); // set/clear output data
  } else if (dionum < 112) { // FPGA
    if (asHigh) {
      syscon[6/2] |= (1 << (dionum-96));
    } else {
      syscon[6/2] &= ~(1 << (dionum-96));
    }
  } else if (dionum < 128) {
    if (asHigh) {
      syscon[0x14/2] = syscon[0x14/2] | (1 << (dionum-112));
    } else {
      syscon[0x14/2] &= ~(1 << (dionum-112));
    }
  } else if (dionum == 128) {
    syscon[1] = (syscon[1] & ~(1<<15)) | ((asHigh?1:0) << 15);
  } else if (dionum == 129) {
    syscon[1] = (syscon[1] & ~(1<<14)) | ((asHigh?1:0) << 14);
  } else if (dionum <= 146) {
    if (asHigh) {
      syscon[0x404/2] |= (1 << (dionum-130));
    } else {
      syscon[0x404/2] &= ~(1 << (dionum-130));
    }
  } else if (dionum <= 161) {
    if (asHigh) {
      syscon[0x406/2] |= (1 << (dionum-146));
    } else {
      syscon[0x406/2] &= ~(1 << (dionum-146));
    }
  }
}

int ts4200_GetDioDir(int dionum) {
  int ret;

  if (dionum < 0 || dionum >= 130 || DioCapabilities[dionum] == DIO_NOT_PRESENT) {
    ret = 0;
  } else if (dionum < 32) { // PA
    ret = (pa[0x18/4] & (1 << dionum)) ? 1 : 0;
  } else if (dionum < 64) { // PB
    ret = (pb[0x18/4] & (1 << (dionum-32))) ? 1 : 0;
  } else if (dionum < 96) { // PC
    ret = (pc[0x18/4] & (1 << (dionum-64))) ? 1 : 0;
  } else if (dionum < 112) { // FPGA
    ret = (syscon[4/2] & (1 << (dionum-96))) ? 1 : 0;
  } else if (dionum < 128) {
    ret = (syscon[0x12/2] & (1 << (dionum-112))) ? 1 : 0;
  } else if (dionum == 128 || dionum == 129) {
    ret = 1;
  } else if (dionum <= 146) {
    ret = (syscon[0x408/2] & (1 << (dionum-130))) ? 1 : 0;
  } else if (dionum <= 161) {
    ret = (syscon[0x40A/2] & (1 << (dionum-146))) ? 1 : 0;
  }

  if (BusCyclesLog & 4) {
    fprintf(stderr,"%d <- ts4200_GetDioDir(%d)\n",ret,dionum);
  }
  return ret;
}

int ts4200_GetDio(int dionum) {
  int ret;

  if (dionum < 0 || dionum >= 130 || DioCapabilities[dionum] == DIO_NOT_PRESENT) {
    ret = 0;
  } else if (dionum < 32) { // PA
    ret = (pa[0x3C/4] & (1 << dionum)) ? 1 : 0;
  } else if (dionum < 64) { // PB
    ret = (pb[0x3C/4] & (1 << (dionum-32))) ? 1 : 0;
  } else if (dionum < 96) { // PC
    ret = (pc[0x3C/4] & (1 << (dionum-64))) ? 1 : 0;
  } else if (dionum < 112) { // FPGA
    ret = (syscon[8/2] & (1 << (dionum-96))) ? 1 : 0;
  } else if (dionum < 128) {
    ret = (syscon[0x16/2] & (1 << (dionum-112))) ? 1 : 0;
  } else if (dionum == 128) {
    ret = (syscon[1] & (1<<15)) ? 1 : 0;
  } else if (dionum == 129) {
    ret = (syscon[1] & (1<<14)) ? 1 : 0;
  } else if (dionum <= 146) {
    ret = (syscon[0x40C/2] & (1 << (dionum-130))) ? 1 : 0;
  } else if (dionum <= 161) {
    ret = (syscon[0x40E/2] & (1 << (dionum-146))) ? 1 : 0;
  }
  if (BusCyclesLog & 4) {
    fprintf(stderr,"%d <- ts4200_GetDio(%d)\n",ret,dionum);
  }
  return ret;
}
#endif
/*
  Author: Michael Schmidt (michael@embeddedARM.com)
 */
