/*
 * rt8139.c
 *
 * Copyright 2004, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * RTL8139 Ethernet controller.
 */


#include<types.h>
#include<pci.h>
#include<device.h>
#include<mm.h>
#include<lib.h>
#include<interrupt.h>
#include<errno.h>
#include<mp.h>
#include<lock.h>
#include<net/net.h>
#include<net/ether.h>
#include<debug.h>


//#define DEBUG_RT8139 1

#ifdef DEBUG_RT8139
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


/*************************************************************************
 *
 * 共通
 *
 *************************************************************************/


enum{
	TX_BUF_NUM = 4,		// 送信バッファ数

	/* Receive buffer size. */
	RX_BUF_SIZE8K	= 1024 * 8,
	RX_BUF_SIZE16K	= 1024 * 16,
	RX_BUF_SIZE32K	= 1024 * 32,
	RX_BUF_SIZE64K	= 1024 * 64,
	RX_BUF_SIZE		= RX_BUF_SIZE32K,	/* 変更可。 */

	/* Receive status bits in buffer. */
	RST_MULTI		= 1 << 15,
	RST_PHYS		= 1 << 14,
	RST_BROAD		= 1 << 13,
	RST_BAD_SYMBOL	= 1 << 5,
	RST_RUNT		= 1 << 4,
	RST_TOO_LONG	= 1 << 3,
	RST_CRC_ERR		= 1 << 2,
	RST_BAD_ALIGN	= 1 << 1,
	RST_OK			= 1 << 0,

	/* Transmit status register. */
	REG_TX_OWN		= 1 << 13,	// host owns
	REG_TX_UNDER	= 1 << 14,	// underrun
	REG_TX_OK		= 1 << 15,	// stat OK
	REG_TX_OUT		= 1 << 29,	// out of window
	REG_TX_ABORT	= 1 << 30,	// aborted
	REG_TX_LOST		= 1 << 31,	// carrier lost

	/* Command register. */
	REG_COM_RST		= 1 << 4,	/* Reset */
	REG_COM_RE		= 1 << 3,	/* Receiver enable. */
	REG_COM_TE		= 1 << 2,	/* Transmitter enable. */
	REG_COM_BUFE	= 1 << 0,	/* Receive buffer empty. */

	/* Transmit configuration register. */
	REG_TRC_MAXDMA	= 7 << 8,	/* Max DMA burst size 2048bytes. */
	REG_TRC_IFG		= 3 << 24,	/* Interframe gap time. */
	REG_TRC_ClRABT	= 1 << 0,	/* Clear abort. */

	/* Receive configration register. */
	REG_RCC_AAP		= 1 << 0,
	REG_RCC_APM		= 1 << 1,
	REG_RCC_AM		= 1 << 2,
	REG_RCC_AB		= 1 << 3,
	REG_RCC_WRAP	= 1 << 7,
	REG_RCC_MAXDMA	= 7 << 8,	/* Unlimited. */
	REG_RCC_RB8K	= 0 << 11,	/* 8K+16byte. */
	REG_RCC_RB16K	= 1 << 11,	/* 16K+16byte. */
	REG_RCC_RB32K	= 2 << 11,	/* 32K+16byte. */
	REG_RCC_RB64K	= 3 << 11,	/* 64K+16byte. */
	REG_RCC_RXFTH	= 7 << 13,	/* No rx threshold. */

	/* Interrupt mask register. */
	REG_INM_ROK		= 1 << 0,
	REG_INM_RER		= 1 << 1,
	REG_INM_TOK		= 1 << 2,
	REG_INM_TER		= 1 << 3,
	REG_INM_RXOVW	= 1 << 4,
	REG_INM_PUN		= 1 << 5,
	REG_INM_FOVW	= 1 << 6,
	REG_INM_LENCH	= 1 << 13,
	REG_INM_TMOUT	= 1 << 14,
	REG_INM_SERR	= 1 << 15,
	REG_INM_ALL		= REG_INM_ROK | REG_INM_RER | REG_INM_TOK | REG_INM_TER | REG_INM_RXOVW |
						REG_INM_PUN | REG_INM_FOVW | REG_INM_TMOUT | REG_INM_SERR,

	/* 93C46 command register. */
	REG_9346_ENB	= 3 << 6,
	REG_9346_DSB	= 0,

	/* Configuration register 1. */
	REG_CONFIG1_DVRLOAD	= 1 << 5,
	REG_CONFIG1_LWACT	= 1 << 4,
	REG_CONFIG1_VPD		= 1 << 0,
};


/* Operation register. */
typedef struct{
	uchar			idr[6];				/* 0x00 */
	ushort reserv0;
	uchar volatile	mar0;				/* 0x08 */
	uchar volatile	mar1;				/* 0x09 */
	uchar volatile	mar2;				/* 0x0a */
	uchar volatile	mar3;				/* 0x0b */
	uchar volatile	mar4;				/* 0x0c */
	uchar volatile	mar5;				/* 0x0d */
	uchar volatile	mar6;				/* 0x0e */
	uchar volatile	mar7;				/* 0x0f */
	uint volatile	tsd[TX_BUF_NUM];	/* 0x10 */
	uint volatile	tsad[TX_BUF_NUM];	/* 0x20 */
	uint volatile	rbstart;			/* 0x30 */
	ushort volatile	erbcr;				/* 0x34 */
	uchar volatile	ersr;				/* 0x36 */
	uchar volatile	cr;					/* 0x37 */
	ushort volatile	capr;				/* 0x38 */
	ushort volatile	cbr;				/* 0x3a */
	ushort volatile	imr;				/* 0x3c */
	ushort volatile	isr;				/* 0x3e */
	uint volatile	tcr;				/* 0x40 */
	uint volatile	rcr;				/* 0x44 */
	uint volatile	tctr;				/* 0x48 */
	uint volatile	mpc;				/* 0x4c */
	uchar volatile	cr9346;				/* 0x50 */
	uchar volatile	config0;			/* 0x51 */
	uchar volatile	config1;			/* 0x52 */
	uchar reserv1;
	uint volatile	timerint;			/* 0x54 */
	uchar volatile	msr;				/* 0x58 */
	uchar volatile	config3;			/* 0x59 */
	uchar volatile	config4;			/* 0x5a */
	uchar reserv2;
	ushort volatile	mulint;				/* 0x5c */
	uchar volatile	rerid;				/* 0x5e */
	uchar reserv3;
	ushort volatile	tsadall;			/* 0x60 */
	ushort volatile	bmcr;				/* 0x62 */
	ushort volatile	bmsr;				/* 0x64 */
	ushort volatile	anar;				/* 0x66 */
	ushort volatile	anlpar;				/* 0x68 */
	ushort volatile	aner;				/* 0x6a */
	ushort volatile	dis;				/* 0x6c */
	ushort volatile	fcsc;				/* 0x6e */
	ushort volatile	nwaytr;				/* 0x70 */
	ushort volatile	rec;				/* 0x72 */
	ushort volatile	cscr;				/* 0x74 */
	ushort reserv4;
	uint volatile	phy1_parm;			/* 0x78 */
	uint volatile	tw_parm;			/* 0x7c */
	uchar volatile	phy2_parm;			/* 0x80 */
	uchar reserv5[3];
	uchar volatile	crc0;				/* 0x84 */
	uchar volatile	crc1;				/* 0x85 */
	uchar volatile	crc2;				/* 0x86 */
	uchar volatile	crc3;				/* 0x87 */
	uchar volatile	crc4;				/* 0x88 */
	uchar volatile	crc5;				/* 0x89 */
	uchar volatile	crc6;				/* 0x8a */
	uchar volatile	crc7;				/* 0x8b */
	uint64 volatile	wakeup0;			/* 0x8c */
	uint64 volatile	wakeup1;			/* 0x94 */
	uint64 volatile	wakeup2;			/* 0x9c */
	uint64 volatile	wakeup3;			/* 0xa4 */
	uint64 volatile	wakeup4;			/* 0xac */
	uint64 volatile	wakeup5;			/* 0xb4 */
	uint64 volatile	wakeup6;			/* 0xbc */
	uint64 volatile	wakeup7;			/* 0xc4 */
	uchar volatile	lsbcrc0;			/* 0xcc */
	uchar volatile	lsbcrc1;			/* 0xcd */
	uchar volatile	lsbcrc2;			/* 0xce */
	uchar volatile	lsbcrc3;			/* 0xcf */
	uchar volatile	lsbcrc4;			/* 0xd0 */
	uchar volatile	lsbcrc5;			/* 0xd1 */
	uchar volatile	lsbcrc6;			/* 0xd2 */
	uchar volatile	lsbcrc7;			/* 0xd3 */
	uint volatile	flash;				/* 0xd4 */
	uchar volatile	sym_err;			/* 0xd8 */
}RT_REG;


/* コントローラごとの固有情報。 */
typedef struct{
	RT_REG			*reg;				/* Register address. */
	uint			 rbuf;				/* Receive buffer address. */
	int				 crtRbuf;			/* Current receive buffer offset. */
	uint			 tbuf[TX_BUF_NUM];	/* Transfer buffer address. */
	int				 tlock;				/* Transfer buffer lock gate. */
	uchar volatile	 txCnt;				/* 送信カウント */
	uchar volatile	 txStatCnt;			/* 送信後割り込みカウント */
	uchar			 mac[6];			/* Mac ID. */
}LOCAL_INFO;


/*
 * フラッシュ書き込み
 * 連続して書き込む場合は読み出しでレジスタをフラッシュすることが必要
 */
static int dummy;
#define WRITE_REG_FLUSH(reg, value)   do{(reg) = (value); (dummy) = (reg);}while (0);


/*************************************************************************
 *
 * 送信
 *
 *************************************************************************/


enum{
	TR_THRESH = (256 << 11) & 0x3f0000,
};


//================================== PROTECTED ==========================================


// 送信結果割り込み確認
STATIC void intrTransfer(LOCAL_INFO *localInfo)
{
	for (;; ++localInfo->txStatCnt){
		int state = localInfo->reg->tsd[localInfo->txStatCnt % TX_BUF_NUM];

		if ((state & (REG_TX_OK | REG_TX_UNDER | REG_TX_ABORT)) == 0){
			break;
		}

		if (state & (REG_TX_OUT | REG_TX_ABORT)){
			localInfo->reg->tcr = REG_TRC_ClRABT | REG_TRC_MAXDMA;
		}
		//送信アンダーランエラー発生、送信時のThreshold値がおかしい？
		else if (state & REG_TX_UNDER){
			printk("RT8139 transfer underrun error!\n");
		}
		
		if (localInfo->txCnt - localInfo->txStatCnt == 0){
			break;
		}
	}
}


/*
 * Transfer ethernet frame.
 */
STATIC void transFrame(struct ifnet *ifnet)
{
	int crt;
	int size;
	TRANS_BUF_INFO *tbi = ifnet->if_snd.ifq_head;
	ETHER_FRAME *frame;
	LOCAL_INFO *localInfo = ifnet->if_softc;

	size = sizeof(ETHER_FRAME) + tbi->size[0] + tbi->size[1] + tbi->size[2];

	enter_spinlock(&localInfo->tlock);
	{
		//送信後割り込みが処理されるまで待つ。
		WAIT_LOOP(TX_BUF_NUM <= (localInfo->txCnt - localInfo->txStatCnt));

		crt = localInfo->txCnt % TX_BUF_NUM;
		frame = (ETHER_FRAME*)localInfo->tbuf[crt];

		// MACアドレス
		memcpy(frame->dstmac, ifnet->if_snd.dstmac, 6);

		frame->type = swapWord(tbi->type);
		memcpy(frame->data, tbi->data[0], tbi->size[0]);
		memcpy(frame->data + tbi->size[0], tbi->data[1], tbi->size[1]);
		memcpy(frame->data + tbi->size[0] + tbi->size[1], tbi->data[2], tbi->size[2]);

		WRITE_REG_FLUSH(localInfo->reg->tsd[crt], MAX(size, ETHER_FRAME_SIZE_MIN) | TR_THRESH);

		++localInfo->txCnt;
	}
	exit_spinlock(&localInfo->tlock);
}


/*
 * Init transfer buffer.
 * return : 0 or error number
 */
STATIC int initTransBuf(LOCAL_INFO *localInfo)
{
	void *mem;
	int i;

	/* Set transfer buffer address. */
	for (i = 0; i < TX_BUF_NUM; ++i){
		if ((mem = kmalloc(ETHER_FRAME_SIZE_MAX)) == NULL){
			return -ENOMEM;
		}
		localInfo->tbuf[i] = localInfo->reg->tsad[i] = (uint)mem;
		memcpy(((ETHER_FRAME*)mem)->srcmac, localInfo->mac, 6);
	}

	return 0;
}


/*
 * Reset transfer registers.
 * return : 0 or error number
 */
STATIC int resetTransfer(LOCAL_INFO *localInfo)
{
	/* Set transmit configration register. */
	localInfo->reg->tcr = REG_TRC_MAXDMA;

	localInfo->txCnt = 0;
	localInfo->txStatCnt = 0;
	localInfo->tlock = 0;

	return 0;
}


/*************************************************************************
 *
 * 受信
 *
 *************************************************************************/


/*
 * PUBLIC
 * Init receive buffer.
 * parameters : LOCAL_INFO address
 * return : 0 or error number
 */
STATIC int initReceiveBuf(LOCAL_INFO *localInfo)
{
	void *mem;
	int memsize;


	// Set receive buffer
	switch(RX_BUF_SIZE)
	{
		case RX_BUF_SIZE8K:
			memsize = 8 * 1024 + 4096;
			break;
		case RX_BUF_SIZE16K:
			memsize = 16 * 1024 + 4096;
			break;
		case RX_BUF_SIZE32K:
			memsize = 32 * 1024 + 4096;
			break;
		case RX_BUF_SIZE64K:
			memsize = 64 * 1024 + 4096;
			break;
		default:
			return -EINVAL;
	}

	// Init receive buffer
	mem = kmalloc(memsize);
	if (mem == NULL){
		return -ENOMEM;
	}
	localInfo->reg->rbstart = (uint)mem;
	localInfo->rbuf = (uint)mem;

	return 0;
}


/*
 * PUBLIC
 * Init receive registers.
 * parameters : register
 */
STATIC void resetReceive(LOCAL_INFO *localInfo)
{
	RT_REG *reg = localInfo->reg; 

	/* Set receive configration register. */
	WRITE_REG_FLUSH(reg->rcr,REG_RCC_MAXDMA|REG_RCC_RXFTH|REG_RCC_APM|REG_RCC_AB|REG_RCC_AM|REG_RCC_WRAP);

	/* Set multicast. */
	WRITE_REG_FLUSH(reg->mar0,0xff);
	WRITE_REG_FLUSH(reg->mar1,0xff);
	WRITE_REG_FLUSH(reg->mar2,0xff);
	WRITE_REG_FLUSH(reg->mar3,0xff);
	WRITE_REG_FLUSH(reg->mar4,0xff);
	WRITE_REG_FLUSH(reg->mar5,0xff);
	WRITE_REG_FLUSH(reg->mar6,0xff);
	WRITE_REG_FLUSH(reg->mar7,0xff);

	/* Set read buffer pointer. */
	switch(RX_BUF_SIZE)
	{
		case RX_BUF_SIZE8K:
			WRITE_REG_FLUSH(reg->rcr,reg->rcr|REG_RCC_RB8K);
			break;
		case RX_BUF_SIZE16K:
			WRITE_REG_FLUSH(reg->rcr,reg->rcr|REG_RCC_RB16K);
			break;
		case RX_BUF_SIZE32K:
			WRITE_REG_FLUSH(reg->rcr,reg->rcr|REG_RCC_RB32K);
			break;
		case RX_BUF_SIZE64K:
			WRITE_REG_FLUSH(reg->rcr,reg->rcr|REG_RCC_RB64K);
			break;
	}

	/* Init packet counter. */
	WRITE_REG_FLUSH(reg->mpc,0);

	/* Clear multiple interrtupt register. */
	WRITE_REG_FLUSH(reg->mulint,reg->mulint&0xf000);

	localInfo->crtRbuf = 0;
}


typedef struct{
	ushort state;		/* Receive state. */
	ushort size;		/* Frame size */
	ETHER_FRAME ether;
}RECEIVE_FRAME;


/*
 * PUBLIC
 * Read receive buffer.
 * parameters : device number,LOCAL_INFO address
 * return : task switch on=1,off=0
 */
STATIC int readReceiveBuf(int dev, LOCAL_INFO *localInfo)
{
	RT_REG *reg = localInfo->reg;
	RECEIVE_FRAME *frame;
	int rest = 0;

	while ((reg->cr & REG_COM_BUFE) == 0){
		frame = (RECEIVE_FRAME*)(localInfo->rbuf + localInfo->crtRbuf);

		// エラー
		if ((frame->state & RST_OK) == 0){
			resetReceive(localInfo);
			break;
		}

		// フレームサイズが0xfff0のときはバスマスタ転送中
		if (frame->size == 0xfff0){
			break;
		}

		// 次の受取のためのレシーブバッファのセット
		localInfo->crtRbuf = ROUNDUP(localInfo->crtRbuf + frame->size + 4, 4);
		localInfo->crtRbuf %= RX_BUF_SIZE;
		reg->capr = localInfo->crtRbuf - 16;

		rest = receiveEther(dev, &frame->ether);
	}

	return rest;
}


/*************************************************************************
 *
 * リセット
 *
 *************************************************************************/


/*
 * PUBLIC
 * Reset rt8139.
 */
STATIC void resetRt(RT_REG *reg)
{
	reg->cr = REG_COM_RST;
	while ((reg->cr & REG_COM_RST) != 0){
		mili_timer(5);
	}
}


/*
 * PUBLIC
 * Init rt8139.
 * parameters : RT_REG address
 * return : 0 or error number
 */
STATIC int initRt(LOCAL_INFO *localInfo)
{
	RT_REG *reg = localInfo->reg;

	/* Soft reset. */
	resetRt(reg);

	/* Enable transfer and receive. */
	reg->cr = REG_COM_RE | REG_COM_TE;

	/* Init receive registers. */
	resetReceive(localInfo);

	/* Init transfer registers. */
	resetTransfer(localInfo);

	/* 割り込みマスクの設定。 */
	reg->imr = REG_INM_ROK | REG_INM_RER | REG_INM_TOK | REG_INM_TER | REG_INM_RXOVW |
			REG_INM_PUN | REG_INM_FOVW | REG_INM_TMOUT | REG_INM_SERR;

	return 0;
}


/**************************************************************************
 *
 * 割り込み
 *
 **************************************************************************/


//================================== PRIVATE ============================================


STATIC LOCAL_INFO *local[2];


/*
 * Interrupt handler.
 * parameters : device number,LOCAL_INFO address
 * return : task switch on=1,off=0
 */
STATIC INLINE int intrHandler(int dev,LOCAL_INFO *localInfo)
{
	int state;
	int rest = 0;
	RT_REG *reg;

	reg = localInfo->reg;

	for(;;)
	{
		state = reg->isr;

		//デバイスは取り去られた？
		if (state == 0xffff){
			break;
		}

		reg->isr = state;

		if ((state & REG_INM_ALL) == 0){
			break;
		}

		if (state & REG_INM_ROK){
			rest = readReceiveBuf(dev,localInfo);
		}
		if (state & (REG_INM_TOK | REG_INM_TER)){
			intrTransfer(localInfo);
		}
		//PCIエラー？
		if (state & REG_INM_SERR){
			initRt(localInfo);
		}
	}

	return rest;
}


/*
 * PRIVATE
 * Interrupt handler head.
 * return : task switch on=1,off=0
 */
STATIC int intr0()
{
	return intrHandler(0,local[0]);
}

STATIC int intr1()
{
	return intrHandler(1,local[1]);
}


/* PUBLIC */
int (*intr[])()={intr0,intr1};


/**************************************************************************
 *
 * 初期化
 *
 **************************************************************************/


enum{
	/* Vender ID,Device ID. */
	VID_REALTEK=					0x10EC,
		DID_REALTEK_8129=			0x8129,
		DID_REALTEK_8139=			0x8139,
	VID_DELTA=						0x1500,
		DID_DELTA_8139=				0x1360,

	/* PCI Configuration Space Registers. */
	RT_PCI_IOAR=	0x10,
	RT_PCI_MEMAR=	0x14,
};


//================================== PRIVATE ============================================


/*
 * Init interrupt.
 * parameters : device number,PCI_INFO address,LOCAL_INFO address
 * return : 0 or error number
 */
STATIC int initIntr(int devnum, PCI_INFO *pci, LOCAL_INFO *localInfo)
{
	int rest;
	int irq;


	/* 割り込みの設定。 */
	irq = read_pci_config(pci, PCI_CONF_INTRLIN) & 0xff;
	local[devnum] = localInfo;
	irq_entry[irq] = intr[devnum];

	if (MFPS_addres){
		// マルチプロセッサ
		if ((rest = SetPciIrqInIoapic(pci, irq)) < 0){
			return rest;
		}
		releasePciIrqIoapicMask(pci);
	}
	else{
		// シングルプロセッサ
		release_irq_mask(irq);
	}

	return 0;
}


/*
 * PCI機能の設定。
 * parameters : PCI_INFO,LOCAL_INFO pointer address
 * return : 0 or error number
 */
STATIC int initPCI(PCI_INFO *pci, LOCAL_INFO *localInfo)
{
	int com;
	uint membase;

	/* ポート・メモリーベース、バスマスターON。 */
	com = read_pci_config(pci, PCI_CONF_COMMAND);
	com |= PCI_COMMAND_IOACS | PCI_COMMAND_MEMACS | PCI_COMMAND_BSMST;
	writew_pci_config(pci, com, PCI_CONF_COMMAND);

	/* レジスタメモリアクセスベースアドレスの設定。 */
	membase = getPciMemAddress(0x100);
	writedw_pci_config(pci, RT_PCI_MEMAR, membase);
	if (read_pci_config(pci, RT_PCI_MEMAR) != membase){
		return -EDERRE;
	}
	localInfo->reg = (RT_REG*)membase;

	return 0;
}


//================================== PUBLIC =============================================


/*
 * parameters : device number
 * return : next device number or error number
 */
int initRt8139(int devnum)
{
	char *nic_name;
	char *dev_name;
	int rest;
	int vendID, devID;
	PCI_INFO pci = {0, 0, 0, 0};


	/* NicをPCIから検索。 */
	while (search_pci_class(PCI_CLASS_ETHER, &pci) == 0)
	{
		/* VenderID と DeviceID の確認。 */
		nic_name = NULL;
		vendID = pci.vidDid & 0xffff;
		devID = pci.vidDid >> 16;
		switch (vendID)
		{
			case VID_REALTEK:
				switch (devID)
				{
					case DID_REALTEK_8129:
						nic_name = "RealTek 8129 10/100BaseTX";
						break;
					case DID_REALTEK_8139:
						nic_name = "RealTek 8139 10/100BaseTX";
						break;
				}
				break;
			case VID_DELTA:
				switch(devID)
				{
					case DID_DELTA_8139:
						nic_name = "Delta Electronics 8139 10/100BaseTX";
						break;
				}
				break;
		}

		if (nic_name!=NULL)
		{
			struct ifnet *ifnet;
			LOCAL_INFO *localInfo;
			
			// Init device information.
			ifnet = kmalloc(sizeof(*ifnet));
			if (ifnet == NULL){
				return -ENOMEM;
			}
			memset(ifnet, 0, sizeof(*ifnet));
			ifnet->if_start = transFrame;
			if ((dev_name = kmalloc(6)) == NULL){
				return -ENOMEM;
			}
			sprintk(dev_name, "eth%d", devnum);
			ifnet->if_name = dev_name;

			localInfo = kmalloc(sizeof(*localInfo));
			if (localInfo == NULL){
				return -ENOMEM;
			}
			/* Init PCI. */
			rest = initPCI(&pci, localInfo);
			if (rest < 0){
				return rest;
			}
			/* Get MAC address. */
			memcpy(localInfo->mac, localInfo->reg->idr, 6);
			/* Init receive buffer. */
			rest = initReceiveBuf(localInfo);
			if (rest < 0){
				return rest;
			}
			/* Init transfer buffer. */
			rest = initTransBuf(localInfo);
			if (rest < 0){
				return rest;
			}
			/* Init chip. */
			initRt(localInfo);
			/* Init interrrupt. */
			if (initIntr(devnum, &pci, localInfo) < 0){
				return devnum;
			}
			/* Set device structure. */
			setEtherDevice(devnum, ifnet, localInfo->mac);
			ifnet->if_softc = localInfo;

			printk("%s : %s\n", ifnet->if_name, nic_name);

			++devnum;
		}

		++pci.dev;
		pci.func = 0;
	}

	return devnum;
}
