/*
	skyeye_mach_ep7312.c - define machine ep7312 for skyeye
	Copyright (C) 2003 Skyeye Develop Group
	for help please send mail to <skyeye-developer@lists.sf.linuxforum.net> 
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
*/
/*
 * 3/24/2003 	init this file.
 * 		add machine ep7312's function. Most taken from original armio.c. 
 * 		include: ep7312_mach_init, ep7312_io_do_cycle
 * 		ep7312_io_read_word, ep7312_io_write_word
 *		walimis <walimi@peoplemail.com.cn> 		
 *
 * */
/*
 * 30/1/2004 modified this file
 * add net support
**/
#include "armdefs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "clps7110.h"
#include "skyeye-ne2k.h"
//zzc:
#ifdef __CYGWIN__
#include "mpw/sys/time.h"
#endif

#define TC_DIVISOR	(50)	/* Set your BogoMips here :) */

extern int skyeye_net_maxfd;
extern int skyeye_net_on;

//ywc 2004-04-01
extern unsigned long Pen_buffer[8]; // defined in skyeye_lcd.c

#define DEBUG 1

#if DEBUG
#define DBG_PRINT(a...) fprintf(stderr,##a)
#else
#define DBG_PRINT(a...)
#endif

//added by ksh
//#define EP7312_NET_BASE	 (0x8000a000)
//#define EP7312_NET_SIZE  255
//#define EP7312_NET_IRQNUM 6

//#define NET_INT EINT2

/*Internal IO Register*/
typedef struct ep7312_io {
	ARMword		syscon;			/* System control */
	ARMword		sysflg;			/* System status flags */

	ARMword		intsr;			/* Interrupt status reg */
	ARMword		intmr;			/* Interrupt mask reg */

	ARMword		tcd[2];			/* Timer/counter data */
	ARMword		tcd_reload[2];		/* Last value written */
	int		tc_prescale;

	ARMword		uartdr;			/* Receive data register */

	ARMword		lcdcon;			/* LCD control */
	/* chy 2004-03-11 */
	ARMword 	lcd_is_enable;		/* LCD is turn on? */
	ARMword		lcd_addr_begin;		/* the begin display mem addr of LCD*/
	ARMword		lcd_addr_end;		/* the begin display mem addr of LCD*/
						/* 0xc0000000 <= LCD buffer < lcd_limit */
	//ywc,2004-04-01
	ARMword		ts_int;                     /* ywc 2004-04-02 */
	ARMword		ts_buffer[8];
	//ARMword		tsaddr;                	/* ??? touch srceen data buffer start address*/
	ARMword 	ts_is_enable;
	ARMword 	ts_addr_begin;
	ARMword 	ts_addr_end;
	

	/*ksh 2004-3-6, for net supported*/
	ARMword	net_int;			/*netcard interrupt bit*/
	ARMword		net_io_baseaddr; /*netcard iomem start addr*/
	ARMword		net_io_size; /*netcard iomem size */
	//ARMword		net_flags;		/*net flags*/

	//2004-06-21
	ARMword		padr;
	ARMword		pbdr;
	ARMword		pddr;
	ARMword		paddr;
	ARMword		pbddr;
	ARMword		pdddr;
	ARMword		pedr;
	ARMword		peddr;
} ep7312_io_t;

static ep7312_io_t ep7312_io;
#define io ep7312_io

static void ep7312_lcd_write(ARMul_State *state, ARMword addr, ARMword data)
{

        lcd_write(state, addr, data);
}

static void ep7312_lcd_disable(ARMul_State *state)
{
        //*(state->mach_io.lcd_enable)=0;
        //skyeye_lcd_begin=-1;
        //skyeye_lcd_limit=0;
        lcd_disable(state);
}

static void ep7312_lcd_enable(ARMul_State *state, int width, int height, int depth)
{
   //skyeye_lcd_enabled=1;
   //io.lcd_limit = LCD_BASE + (width * height * depth / 8);
   //skyeye_lcd_begin=LCD_BASE;
   //skyeye_lcd_limit=io.lcd_limit;
   lcd_enable(state, width, height, depth);
}

static void ep7312_update_lcd(ARMul_State *state)
{
        ep7312_lcd_disable(state);
	// by ywc 2004-07-24 for test
        //if (io.syscon & LCDEN) {
        if (1) {
                ARMword lcdcon = io.lcdcon;
                ARMword vbufsiz = lcdcon & VBUFSIZ;

                ARMword linelen = (lcdcon & LINELEN) >> LINELEN_SHIFT;
                int width, height, depth;
/*
                switch (lcdcon & (GSEN|GSMD)) {
                case GSEN:
                        depth = 2;
                        break;
                case GSEN|GSMD:
                        depth = 4;
                        break;
                default:
                        depth = 1;
                        break;
                }
*/
                depth=8;//test by ywc 2004-07-24

		width = (linelen + 1) * 16;

                if(linelen==0x3b){
			
			depth=12;    //test by ywc 2004-10-14
		
			width = (linelen + 1) * 16/3;
		}
                              
           	height = (vbufsiz + 1) * 128 / depth / width;  //for 1b,2b,4b,8b, the register is big enough for the size 
                ep7312_lcd_enable(state, width, height, depth);
        }
}


static void ep7312_update_int(ARMul_State *state)
{
	ARMword	requests = io.intsr & io.intmr;
	state->NfiqSig = (requests & 0x0001) ? LOW : HIGH;
	state->NirqSig = (requests & 0xfffe) ? LOW : HIGH;
}
static void ep7312_io_reset(ARMul_State *state)
{
	io.syscon = 0;
	io.sysflg = URXFE;
	io.intmr = 0;
	
	io.intsr = UTXINT;	/* always ready to transmit */
	
	io.tcd[0] = 0xffff;
	io.tcd[1] = 0xffff;
	io.tcd_reload[0] = 0xffff;
	io.tcd_reload[1] = 0xffff;
	io.tc_prescale = TC_DIVISOR;
	io.uartdr = 0;
	io.lcdcon = 0;
	//chy 2004-03-11
	io.lcd_is_enable 	   =0;
	io.lcd_addr_begin  =0xc0000000;
	io.lcd_addr_end    =0xc0000000;
	
	//ywc 2004-04-01
	//io.tsaddr		=0x0;
	io.ts_int		=EINT2; /* ywc 2004-04-02 use EINT2 as touch screen interrupt */
	io.ts_is_enable		=1;
	io.ts_addr_begin  	=0x8000b000;
	io.ts_addr_end    	=0x8000b01f;
  
	 io.net_int = EINT1;/*here we use EINT1 as netcard interrupt*/
	 io.net_io_baseaddr = 0x8000a000; /* netcard iomeme start address*/
	 io.net_io_size = 255;        /* netcard iomeme size*/
	//state->Exception = TRUE;
}
void ep7312_io_do_cycle(ARMul_State *state)
{
	int t;

        io.tc_prescale--;
        if (io.tc_prescale < 0) {
                io.tc_prescale = TC_DIVISOR;

                for (t = 0; t < 2; t++) {
                        if (io.tcd[t] == 0) {

                                if (io.syscon & (t ? TC2M : TC1M)) {

                                        io.tcd[t] = io.tcd_reload[t];
                                } else {
                                        io.tcd[t] = 0xffff;
                                }
                                io.intsr |= (t ? TC2OI : TC1OI);
                                ep7312_update_int(state);
                        } else {
                                io.tcd[t]--;
                        }
                }
		if (!(io.intsr & URXINT))
                {
                        fd_set rfds;
                        struct timeval tv;

                        FD_ZERO(&rfds);
                        FD_SET(skyeye_config.uart.fd_in, &rfds);
                        tv.tv_sec = 0;
                        tv.tv_usec = 0;

                        if (select(skyeye_config.uart.fd_in+1, &rfds, NULL, NULL, &tv) == 1) {
                                unsigned char buf;
                                int n;
                                n = read(skyeye_config.uart.fd_in, &buf, 1);

                                if (n) {
                                        io.uartdr = (int)buf;


                                        io.sysflg &= ~URXFE;
                                        io.intsr |= URXINT;
                                        ep7312_update_int(state);
                                }
                        }
                }//if (!(io.intsr & URXINT))
		
		{
			int ret;
			//modified by ksh,here we set EINT1 to netcard interrupt
			if(skyeye_net_on && !(io.intsr & io.net_int)){
				fd_set frds;
				struct timeval tv;
				FD_ZERO(&frds);		
				skyeye_net_fdset(&frds);
				tv.tv_sec = 0;
				tv.tv_usec = 0;
				ret = 0;
				if((ret = select(skyeye_net_maxfd+1,&frds,NULL,NULL,&tv))>0){
					skyeye_net_input(&frds,state);
				}
			}
		}
		
		// ywc 2004-04-01 for touch screen interrupt
		{
			// ywc 2004-04-01
             
		if(!(io.intsr&io.ts_int)){ //if now has no ts interrupt,then query 
	              if(Pen_buffer[6]==1){  //should trigger a interrupt
				*(state->mach_io.ts_buffer+0)=Pen_buffer[0];
				*(state->mach_io.ts_buffer+1)=Pen_buffer[1];
		//		*(state->mach_io.ts_buffer+2)=Pen_buffer[2];
		//		*(state->mach_io.ts_buffer+3)=Pen_buffer[3];
				*(state->mach_io.ts_buffer+4)=Pen_buffer[4];
		//		*(state->mach_io.ts_buffer+5)=Pen_buffer[5];
				*(state->mach_io.ts_buffer+6)=Pen_buffer[6];
		//		*(state->mach_io.ts_buffer+7)=Pen_buffer[7];
	/*				
				printf("Skyeye: skyeye_mach_ep7312.c: ep7312_io_do_cycle(): *(state->mach_io.ts_buffer+0)=%d\n",*(state->mach_io.ts_buffer+0));
				printf("Skyeye: skyeye_mach_ep7312.c: ep7312_io_do_cycle(): *(state->mach_io.ts_buffer+1)=%d\n",*(state->mach_io.ts_buffer+1));
				printf("Skyeye: skyeye_mach_ep7312.c: ep7312_io_do_cycle(): *(state->mach_io.ts_buffer+2)=%d\n",*(state->mach_io.ts_buffer+2));
				printf("Skyeye: skyeye_mach_ep7312.c: ep7312_io_do_cycle(): *(state->mach_io.ts_buffer+3)=%d\n\n",*(state->mach_io.ts_buffer+3));
	*/		        
        //			printf("\nbefore set ENIT2 bit,io.intsr=%x",io.intsr);	
				io.intsr|= io.ts_int;//set EINT2 bit to trigger a interrupt,ts driver will clear it
        //			printf("\nafter set ENIT2 bit,io.intsr=%x\n",io.intsr);	
				
			Pen_buffer[6]=0;
        		}
		 }
	     }
		//chy 2004-03-11
		lcd_cycle(state);  //by ywc 3-15
	}
}


ARMword ep7312_io_read_byte(ARMul_State *state, ARMword addr)
{
	ARMword data = -1;
  unsigned char offset = 0;
  unsigned char ret;
  if(addr >= io.net_io_baseaddr && addr < (io.net_io_baseaddr + io.net_io_size)){
    offset = addr- io.net_io_baseaddr;
   // DBG_PRINT("nic read begin,offset 0x%x\n",offset);
    ret = nic_read(0,state,offset>>2);
    data = ret;
    return data;
  }else{
	switch(addr-0x80000000){
        	case PADR:
			data=io.padr;
	        	break;
		case PBDR:
			data=io.pbdr;
	        	break;
        	case PDDR:
			data=io.pddr;
	        	break;
        	case PADDR:
			data=io.paddr;
	        	break;
	        case PBDDR:
			data=io.pbddr;
	        	break;
        	case PDDDR:
			data=io.pdddr;
	        	break;
	        case PEDR:
			data=io.pedr;
	        	break;
        	case PEDDR:
			data=io.peddr;
	        	break;
		default:	
			printf("SKYEYE: ep7312_io_read_byte error\n");
			printf("SKYEYE: state->pc=%x,state->instr=%x,addr=%x\n",state->pc,state->instr,addr);
			exit(-1);
			break;
	}
	}
	return data;
}
ARMword ep7312_io_read_halfword(ARMul_State *state, ARMword addr)
{
	printf("SKYEYE: ep7312_io_read_halfword error\n");
        exit(-1);
}
ARMword ep7312_io_read_word(ARMul_State *state, ARMword addr)
{
	ARMword data = 0;
	ARMword ts_addr;

	if(addr >= io.net_io_baseaddr && addr < (io.net_io_baseaddr + io.net_io_size)){
		printf("SKYEYE:ep7312_io_read_word error,should use read_byte for net\n");
		SKYEYE_OUTREGS(stderr);
		exit(-1);
	}
	//ywc 2004-04-01 read the touch srceen data buffer,return to the ts device driver
	ts_addr=addr & ~3;   // 1 word==4 byte
	if(ts_addr >= io.ts_addr_begin && ts_addr <= io.ts_addr_end){
		data=io.ts_buffer[(ts_addr-io.ts_addr_begin)/4];
	//	printf("SKYEYE:ep7312_io_read_word:\n");
        //        printf("addr=%p,ts_addr=%p\n",addr,ts_addr);
                //printf("&io.ts_buffer[%d]=%p\n",(ts_addr-io.ts_addr_begin)/4,&io.ts_buffer[(ts_addr-io.ts_addr_begin)/4]);
          //      printf("data=%d\n",data);
		return data;
	}
	switch (addr - 0x80000000) {
	case SYSCON:
		data = io.syscon;
		break;
	case SYSFLG:
		data = io.sysflg;
		break;
/*	case MEMCFG1:
 *	case MEMCFG2:
 *	case DRFPR */
	case INTSR:
		data = io.intsr;
		break;
	case INTMR:
		data = io.intmr;
		break;
	case LCDCON:
		data = io.lcdcon;
		break;
	case TC1D:
		data = io.tcd[0];
		break;
	case TC2D:
		data = io.tcd[1];
		break;
/*	case RTCDR:
 *	case RTCMR:
 *	case PMPCON :
 *	case CODR:*/
	case UARTDR:
		data = io.uartdr;
		io.sysflg |= URXFE;
		io.intsr &= ~URXINT;
		ep7312_update_int(state);
		break;
/*	case UBRLCR:		*/
	case SYNCIO:
		/* if we return zero here, the battery voltage calculation
 * 	   results in a divide-by-zero that messes up the kernel */
		data = 1;
		break;
/*	case PALLSW:
 *	case PALMSW:*/

	/* write-only: */
	case STFCLR:
	case BLEOI:
	case MCEOI:
	case TEOI:
	case TC1EOI:
	case TC2EOI:
	case RTCEOI:
	case UMSEOI:
	case COEOI:
	case HALT:
	case STDBY:
		break;
	default:
//chy 2003-07-11: sometime has fault, but linux can continue running  !!!!????
		//SKYEYE_DBG("io_read_word(0x%08x) = 0x%08x\n", addr, data);
		break;
	}
	return data;
}



void ep7312_io_write_byte(ARMul_State *state, ARMword addr, ARMword data)
{
		//fprintf(stderr,"skyeye:ep7312_io_write_byte,@addr is  %x",addr);
		unsigned char offset = 0;
		unsigned char ret;
		if(addr >= io.net_io_baseaddr && addr < (io.net_io_baseaddr+io.net_io_size)){
			offset = addr - io.net_io_baseaddr;
			if(data > 256){
				fprintf(stderr,"skyeye:write data to nic is bigger than 256");
				SKYEYE_OUTREGS(stderr);
			}
			ret = (unsigned char)data;
			nic_write(0,state,offset>>2,ret);
			return;
		}else{
			switch(addr-0x80000000){
        			case PADR:
					io.padr=data;
	        			break;
				case PBDR:
					io.pbdr=data;
	        			break;
        			case PDDR:
					io.pddr=data;
	        			break;
        			case PADDR:
					io.paddr=data;
	        			break;
	        		case PBDDR:
					io.pbddr=data;
	        			break;
		        	case PDDDR:
					io.pdddr=data;
			        	break;
			        case PEDR:
					data=io.pedr;
			        	break;
		        	case PEDDR:
					io.peddr=data;
			        	break;
				default:
					printf("SKYEYE: ep7312_io_write_byte error@@@@@@@\n");
					SKYEYE_OUTREGS(stderr);
   					exit(-1);
   					break;
   			}
   			
	}
}

void ep7312_io_write_halfword(ARMul_State *state, ARMword addr, ARMword data)
{
	printf("SKYEYE: ep7312_io_write_halfword error\n");
        exit(-1);
}

void ep7312_io_write_word(ARMul_State *state, ARMword addr, ARMword data)
{       
	ARMword tmp;
	if(addr >= io.net_io_baseaddr && addr < (io.net_io_baseaddr + io.net_io_size)){
		printf("SKYEYE:ep7312_io_write_word error,should use write_byte for net\n");
		SKYEYE_OUTREGS(stderr);
		exit(-1);
	}

	switch (addr - 0x80000000) {
	case SYSCON:
		tmp = io.syscon;
		io.syscon = data;
                //chy 2004-03-11
                if ((tmp & LCDEN) != (data & LCDEN)) {
                        // by ywc 2004-07-07
			//printf("\n\n SYSCON:will call ep7312_update_lcd()");
			ep7312_update_lcd(state);
                }

		/*if ((tmp & LCDEN) != (data & LCDEN)) {
			update_lcd(state);
		}*/
		break;
	case SYSFLG:
		break;
	case INTSR:
		//DBG_PRINT("write INTSR=0x%x\n", data);
                io.intsr=data;
	//	printf("SKYEYE: write INTSR=0x%x\n", io.intsr);
		break;
	case INTMR:
		//DBG_PRINT("write INTMR=0x%x\n", data);
		//if(data != 0x2000 && data != 0x2200)
			//printf("SKYEYE: write INTMR=0x%x\n", data);
		io.intmr = data;
		ep7312_update_int(state);
		break;
	case LCDCON:
		//printf("\nio.lcdcon=%x,data=%x",io.lcdcon,data);
		//printf("\n");
		tmp = io.lcdcon;
		io.lcdcon = data;
		//chy 2004-03-11 tmp compare with data
                //if ((tmp & (VBUFSIZ|LINELEN|GSEN|GSMD)) != (data & (VBUFSIZ|LINELEN|GSEN|GSMD))) {
                if ((tmp) != (data)) {
                //if (tmp != data) {
			printf("\n\n LCDCON:will call ep7312_update_lcd()");
                        ep7312_update_lcd(state);
                }
		break;
	case TC1D:
		io.tcd[0] = io.tcd_reload[0] = data & 0xffff;
		SKYEYE_DBG("TC1D 0x%x\n", data & 0xffff);
		break;
	case TC2D:
		io.tcd[1] = io.tcd_reload[1] = data & 0xffff;
		SKYEYE_DBG("TC2D 0x%x\n", data & 0xffff);
		break;
	case UARTDR:
		/* The UART writes chars to console */
		{
			char c = data;
			write(skyeye_config.uart.fd_out, &c, 1);
		}
		break;
/*	case BLEOI: printf("BLEOI\n"); break;
	case MCEOI: printf("MCEOI\n"); break;
	case TEOI: printf("TEOI\n"); break;*/
	case TC1EOI:
		io.intsr &= ~TC1OI;
		ep7312_update_int(state);
		SKYEYE_DBG("TC1EOI\n");
		break;
	case TC2EOI:
		io.intsr &= ~TC2OI;
		ep7312_update_int(state);
		SKYEYE_DBG("TC2EOI\n");
		break;
	//case RTCEOI: printf("RTCEOI\n"); break;
	//case UMSEOI: printf("UMSEOI\n"); break;
	//case COEOI: printf("COEOI\n"); break;
	case 0x2000:
		/* Not a real register, for debugging only: */
		SKYEYE_DBG("io_write_word debug: 0x%08lx\n", data);
		break;
	default:
//chy 2003-07-11: sometime has fault, but linux can continue running  !!!!????
		  //printf("SKYEYE:unknown io addr, io_write_word(0x%08x, 0x%08x), pc %x \n", addr, data,state->Reg[15]);
		break;
	}
}

void ep7312_mach_init(ARMul_State *state, machine_config_t *this_mach)
{
        //chy 2003-08-19, setprocessor
        ARMul_SelectProcessor(state, ARM_v4_Prop);
        //chy 2004-05-09, set lateabtSig
        state->lateabtSig = HIGH;

	state->Reg[1] = 91; //for EP7212/EP7312 arch id
	this_mach->mach_io_do_cycle = 		ep7312_io_do_cycle;
	this_mach->mach_io_reset = 		ep7312_io_reset;
        this_mach->mach_io_read_byte = 		ep7312_io_read_byte;
        this_mach->mach_io_write_byte = 	ep7312_io_write_byte;
        this_mach->mach_io_read_halfword = 	ep7312_io_read_halfword;
        this_mach->mach_io_write_halfword = 	ep7312_io_write_halfword;
	this_mach->mach_io_read_word = 		ep7312_io_read_word;
	this_mach->mach_io_write_word = 	ep7312_io_write_word;
	
	this_mach->mach_update_int =            ep7312_update_int;

	//add by ksh
	//state->interrupt = (ARMword *)&io.intsr;
	state->mach_io.instr = (ARMword *)&io.intsr;
	//state->io->net_flags = (ARMword *)&io.net_flags;
	state->mach_io.net_int = (ARMword *)&io.net_int;
	
	/*chy 2004-03-11*/
	this_mach->mach_lcd_write	=ep7312_lcd_write;
	state->mach_io.lcd_is_enable	=(ARMword *)&io.lcd_is_enable;	
	state->mach_io.lcd_addr_begin	=(ARMword *)&io.lcd_addr_begin;	
	state->mach_io.lcd_addr_end	=(ARMword *)&io.lcd_addr_end;
	
	/*ywc 2004-04-01*/
	state->mach_io.ts_int		=(ARMword *)&io.ts_int;
	state->mach_io.ts_is_enable	=(ARMword *)&io.ts_is_enable;	
	state->mach_io.ts_addr_begin	=(ARMword *)&io.ts_addr_begin;	
	state->mach_io.ts_addr_end	=(ARMword *)&io.ts_addr_end;
	state->mach_io.ts_buffer	=(ARMword *)&io.ts_buffer[0];
}
