/* ne2k_cbus.h: 
   vender-specific information definition for NEC PC-9800
   C-bus Ethernet Cards
   Used in ne.c 

   (C)1998,1999 KITAGWA Takurou & Linux/98 project
*/

#include <linux/config.h>

#if LINUX_VERSION_CODE >= 0x2030b /* KERNEL_VERSION(2,3,11) */
#define CONFIG_RESOURCE98
#endif

#ifdef CONFIG_RESOURCE98
#define SPARSE_REGION(n) (-n)
#else
#define SPARSE_REGION(n) (n)
#endif

/* Hardware type definition (derived from *BSD) */
#define NE2K_CBUS_HARDWARE_TYPE_MASK 0xff

/* 0: reserved for auto-detect */
/* 1: (not tested)
   Allied Telesis CentreCom LA-98-T */
#define NE2K_CBUS_HARDWARE_TYPE_ATLA98 1
/* 2: (not tested)
   ELECOM Laneed
   LD-BDN[123]A
   PLANET SMART COM 98 EN-2298-C
   MACNICA ME98 */
#define NE2K_CBUS_HARDWARE_TYPE_BDN 2
/* 3:
   Melco EGY-98
   Contec C-NET(98)E*A/L*A,C-NET(98)P */
#define NE2K_CBUS_HARDWARE_TYPE_EGY98 3
/* 4:
   Melco LGY-98,IND-SP,IND-SS
   MACNICA NE2098 */
#define NE2K_CBUS_HARDWARE_TYPE_LGY98 4
/* 5:
   ICM DT-ET-25,DT-ET-T5,IF-2766ET,IF-2771ET
   PLANET SMART COM 98 EN-2298-T,EN-2298P-T
   D-Link DE-298PT,DE-298PCAT
   ELECOM Laneed LD-98P */
#define NE2K_CBUS_HARDWARE_TYPE_ICM 5
/* 6: (reserved for SIC-98, which is not supported in this driver.) */
/* 7: (unused in *BSD?)
   <Original NE2000 compatible>
   <for PCI/PCMCIA cards>
*/
#define NE2K_CBUS_HARDWARE_TYPE_NE2K 7
/* 8: (not tested)
   NEC PC-9801-108 */
#define NE2K_CBUS_HARDWARE_TYPE_NEC108 8
/* 9:
   I-O DATA LA-98,LA/T-98 */
#define NE2K_CBUS_HARDWARE_TYPE_IOLA98 9
/* 10: (reserved for C-NET(98), which is not supported in this driver.) */
/* 11:
   Contec C-NET(98)E,L */
#define NE2K_CBUS_HARDWARE_TYPE_CNET98EL 11

#define NE2K_CBUS_HARDWARE_TYPE_MAX 11

/* HARDWARE TYPE ID 12-31: reserved */

struct ne2k_cbus_offsetinfo {
	unsigned short skip;
	unsigned short offset8; /* +0x8 - +0xf */
	unsigned short offset10; /* +0x10 */
	unsigned short offset1f; /* +0x1f */
};

struct ne2k_cbus_region {
	unsigned short start;
	short range;
};

struct ne2k_cbus_hwinfo {
	const unsigned short hwtype;
	const unsigned char *hwident;
#ifndef MODULE
	const unsigned short *portlist;
#endif
	const struct ne2k_cbus_offsetinfo *offsetinfo;
	const struct ne2k_cbus_region *regionlist;
};

/* XXX */
/* we can't use first __initdata with const? */
static struct {} ne2k_cbus_dummy_for_initdata __attribute__((unused)) __initdata = {};

#ifdef CONFIG_NE2K_CBUS_ATLA98
#ifndef MODULE
static const unsigned short atla98_portlist[] __initdata = {
	0xd0,
	0
};
#endif
#define atla98_offsetinfo ne2k_offsetinfo
#define atla98_regionlist ne2k_regionlist
#endif /* CONFIG_NE2K_CBUS_ATLA98 */

#ifdef CONFIG_NE2K_CBUS_BDN
#ifndef MODULE
static const unsigned short bdn_portlist[] __initdata = {
	0xd0,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo bdn_offsetinfo __initdata = {
#if 0
	/* comes from FreeBSD(98) ed98.h */
	0x1000, 0x8000, 0x100, 0xc200 /* ??? */
#else
	/* comes from NetBSD/pc98 if_ne_isa.c */
	0x1000, 0x8000, 0x100, 0x7f00 /* ??? */
#endif
};
static const struct ne2k_cbus_region bdn_regionlist[] __initdata = {
	{0x0,1},{0x1000,1},{0x2000,1},{0x3000,1},
	{0x4000,1},{0x5000,1},{0x6000,1},{0x7000,1},
	{0x8000,1},{0x9000,1},{0xa000,1},{0xb000,1},
	{0xc000,1},{0xd000,1},{0xe000,1},{0xf000,1},{0x100,1},{0x7f00,1},
	{0x0,0}
};
#endif /* CONFIG_NE2K_CBUS_BDN */

#ifdef CONFIG_NE2K_CBUS_EGY98
#ifndef MODULE
static const unsigned short egy98_portlist[] __initdata = {
	0xd0,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo egy98_offsetinfo __initdata = {
	0x02, 0x100, 0x200, 0x300
};
static const struct ne2k_cbus_region egy98_regionlist[] __initdata = {
	{0x0,SPARSE_REGION(16)}, {0x100,SPARSE_REGION(16)},
	{0x200,1}, {0x300,1},
	{0x0,0}
};
#endif /* CONFIG_NE2K_CBUS_EGY98 */

#ifdef CONFIG_NE2K_CBUS_LGY98
#ifndef MODULE
static const unsigned short lgy98_portlist[] __initdata = {
	0xd0, 0x10d0, 0x20d0, 0x30d0, 0x40d0, 0x50d0, 0x60d0, 0x70d0,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo lgy98_offsetinfo __initdata = {
	0x01, 0x08, 0x200, 0x300
};
static const struct ne2k_cbus_region lgy98_regionlist[] __initdata = {
	{0x0,16}, {0x200,1}, {0x300,1},
	{0x0,0}
};
#endif /* CONFIG_NE2K_CBUS_LGY98 */

#ifdef CONFIG_NE2K_CBUS_ICM
#ifndef MODULE
static const unsigned short icm_portlist[] __initdata = {
	/* ICM */
	0x56d0,
	/* LD-98PT */
	0x46d0, 0x66d0, 0x76d0, 0x86d0, 0x96d0, 0xa6d0, 0xb6d0, 0xc6d0,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo icm_offsetinfo __initdata = {
	0x01, 0x08, 0x100, 0x10f
};
static const struct ne2k_cbus_region icm_regionlist[] __initdata = {
	{0x0,16}, {0x100,16},
	{0x0,0}
};
#endif /* CONFIG_NE2K_CBUS_ICM */

#if defined(CONFIG_NE2K_CBUS_NE2K) && !defined(MODULE)
static const unsigned short ne2k_portlist[] __initdata = {
	0xd0, 0x300, 0x280, 0x320, 0x340, 0x360, 0x380,
	0
};
#endif
#if defined(CONFIG_NE2K_CBUS_NE2K) || defined(CONFIG_NE2K_CBUS_ATLA98)
static const struct ne2k_cbus_offsetinfo ne2k_offsetinfo __initdata = {
	0x01, 0x08, 0x10, 0x1f
};
static const struct ne2k_cbus_region ne2k_regionlist[] __initdata = {
	{0x0,32},
	{0x0,0}
};
#endif

#ifdef CONFIG_NE2K_CBUS_NEC108
#ifndef MODULE
static const unsigned short nec108_portlist[] __initdata = {
    0x770, 0x2770, 0x4770, 0x6770,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo nec108_offsetinfo __initdata = {
	0x02, 0x1000, 0x888, 0x88a
};
static const struct ne2k_cbus_region nec108_regionlist[] __initdata = {
	{0x0,SPARSE_REGION(16)}, {0x1000,SPARSE_REGION(16)},
	{0x118,SPARSE_REGION(8)},
	{0x0,0}
};
#endif

#ifdef CONFIG_NE2K_CBUS_IOLA98
#ifndef MODULE
static const unsigned short iola98_portlist[] __initdata = {
	0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo iola98_offsetinfo __initdata = {
	0x1000, 0x8000, 0x100, 0xf100
};
static const struct ne2k_cbus_region iola98_regionlist[] __initdata = {
	{0x0,1},{0x1000,1},{0x2000,1},{0x3000,1},
	{0x4000,1},{0x5000,1},{0x6000,1},{0x7000,1},
	{0x8000,1},{0x9000,1},{0xa000,1},{0xb000,1},
	{0xc000,1},{0xd000,1},{0xe000,1},{0xf000,1},{0x100,1},{0xf100,1},
	{0x0,0}
};
#endif /* CONFIG_NE2K_CBUS_IOLA98 */

#ifdef CONFIG_NE2K_CBUS_CNET98EL
#ifndef MODULE
static const unsigned short cnet98el_portlist[] __initdata = {
	0x3d0, 0x13d0, 0x23d0, 0x33d0, 0x43d0, 0x53d0, 0x60d0, 0x70d0,
	0
};
#endif
static const struct ne2k_cbus_offsetinfo cnet98el_offsetinfo __initdata = {
	0x01, 0x08, 0x40e, 0x400
};
static const struct ne2k_cbus_region cnet98el_regionlist[] __initdata = {
	{0x0, 16}, {0x400, 16},
	{0x0,0}
};
#endif


/* port information table (for ne.c initialize/probe process) */

static const struct ne2k_cbus_hwinfo ne2k_cbus_hwinfo_list[] __initdata = {
#ifdef CONFIG_NE2K_CBUS_ATLA98
/* NOT TESTED */
	{
		NE2K_CBUS_HARDWARE_TYPE_ATLA98,
		"LA-98-T",
#ifndef MODULE
		atla98_portlist,
#endif
		&atla98_offsetinfo, atla98_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_BDN
/* NOT TESTED */
	{
		NE2K_CBUS_HARDWARE_TYPE_BDN,
		"LD-BDN[123]A",
#ifndef MODULE
		bdn_portlist,
#endif
		&bdn_offsetinfo, bdn_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_ICM
	{
		NE2K_CBUS_HARDWARE_TYPE_ICM,
		"IF-27xxET",
#ifndef MODULE
		icm_portlist,
#endif
		&icm_offsetinfo, icm_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_NE2K
	{
		NE2K_CBUS_HARDWARE_TYPE_NE2K,
		"NE2000 compat.",
#ifndef MODULE
		ne2k_portlist,
#endif
		&ne2k_offsetinfo, ne2k_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_NEC108
/* NOT supported yet! */
	{
		NE2K_CBUS_HARDWARE_TYPE_NEC108,
		"PC-9801-108",
#ifndef MODULE
		nec108_portlist,
#endif
		&nec108_offsetinfo, nec108_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_IOLA98
	{
		NE2K_CBUS_HARDWARE_TYPE_IOLA98,
		"LA-98",
#ifndef MODULE
		iola98_portlist,
#endif
		&iola98_offsetinfo, iola98_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_CNET98EL
	{
		NE2K_CBUS_HARDWARE_TYPE_CNET98EL,
		"C-NET(98)E/L",
#ifndef MODULE
		cnet98el_portlist,
#endif
		&cnet98el_offsetinfo, cnet98el_regionlist
	},
#endif
/* NOTE: LGY98 must be probed before EGY98, or system stalled!? */
#ifdef CONFIG_NE2K_CBUS_LGY98
	{
		NE2K_CBUS_HARDWARE_TYPE_LGY98,
		"LGY-98",
#ifndef MODULE
		lgy98_portlist,
#endif
		&lgy98_offsetinfo, lgy98_regionlist
	},
#endif
#ifdef CONFIG_NE2K_CBUS_EGY98
	{
		NE2K_CBUS_HARDWARE_TYPE_EGY98,
		"EGY-98",
#ifndef MODULE
		egy98_portlist,
#endif
		&egy98_offsetinfo, egy98_regionlist
	},
#endif
	{
		0,"unsupported hardware",
#ifndef MODULE
		NULL,
#endif
		NULL,NULL
	}
};

static int __init ne2k_cbus_init(struct net_device *dev)
{
	struct ei_device *ei_local;
	if(dev->priv == NULL) {
		ei_local = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
		if (ei_local == NULL)
			return -ENOMEM;
		memset(ei_local, 0, sizeof(struct ei_device));
		ei_local->reg_offset = kmalloc(sizeof(typeof(*ei_local->reg_offset))*18, GFP_KERNEL);
		if (ei_local->reg_offset == NULL){
			kfree(ei_local);
			return -ENOMEM;
		}
		spin_lock_init(&ei_local->page_lock);
		dev->priv = ei_local;
	}
	return 0;
}

static void ne2k_cbus_destroy(struct net_device *dev)
{
	struct ei_device *ei_local = (struct ei_device *)(dev->priv);
	if(ei_local != NULL){
		if(ei_local->reg_offset)
			kfree(ei_local->reg_offset);
		kfree(dev->priv);
		dev->priv=NULL;
	}
}

static const struct ne2k_cbus_hwinfo * __init ne2k_cbus_get_hwinfo(int hwtype)
{
	const struct ne2k_cbus_hwinfo *hw;

	for(hw=&ne2k_cbus_hwinfo_list[0]; hw->hwtype; hw++){
		if(hw->hwtype==hwtype) break;
	}
	return hw;
}

static void __init ne2k_cbus_set_hwtype(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr)
{
	struct ei_device *ei_local=(struct ei_device *)(dev->priv);
	int i;
	int hwtype_old=(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK);

	if (!ei_local)
		panic("Gieee! ei_local == NULL!! (from %p)",
		       __builtin_return_address(0));

	dev->mem_start &= (~NE2K_CBUS_HARDWARE_TYPE_MASK);
	dev->mem_start |= (hw->hwtype & NE2K_CBUS_HARDWARE_TYPE_MASK);

	if(ei_debug>2){
		printk(KERN_DEBUG "hwtype changed: %d -> %d\n",hwtype_old,(int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
	}

	if(hw->offsetinfo){
		for(i=0;i<8;i++){
			ei_local->reg_offset[i] = hw->offsetinfo->skip * i;
		}
		for(i=8;i<16;i++){
			ei_local->reg_offset[i] =
				hw->offsetinfo->skip*(i-8) + hw->offsetinfo->offset8;
		}
#ifdef CONFIG_NE2K_CBUS_NEC108
		if(hw->hwtype==NE2K_CBUS_HARDWARE_TYPE_NEC108){
			int adj = (ioaddr & 0xf000) /2;
			ei_local->reg_offset[16] = 
				(hw->offsetinfo->offset10 | adj) - ioaddr;
			ei_local->reg_offset[17] = 
				(hw->offsetinfo->offset1f | adj) - ioaddr;
		}else{
#endif /* CONFIG_NE2K_CBUS_NEC108 */
			ei_local->reg_offset[16] = hw->offsetinfo->offset10;
			ei_local->reg_offset[17] = hw->offsetinfo->offset1f;
#ifdef CONFIG_NE2K_CBUS_NEC108
		}
#endif
	}else{
		/* make dummmy offset list */
		for(i=0;i<16;i++){
			ei_local->reg_offset[i] = i;
		}
		ei_local->reg_offset[16] = 0x10;
		ei_local->reg_offset[17] = 0x1f;
	}
}

#if defined(CONFIG_NE2K_CBUS_ICM) || defined(CONFIG_NE2K_CBUS_CNET98EL)
static void __init ne2k_cbus_readmem(struct net_device *dev, int ioaddr, unsigned short memaddr, char *buf, unsigned short len)
{
	struct ei_device *ei_local = (struct ei_device *)(dev->priv);
	outb_p(E8390_NODMA | E8390_START, ioaddr+E8390_CMD);
	outb_p(               len & 0xff, ioaddr+EN0_RCNTLO);
	outb_p(               len >> 8  , ioaddr+EN0_RCNTHI);
	outb_p(           memaddr & 0xff, ioaddr+EN0_RSARLO);
	outb_p(           memaddr >> 8  , ioaddr+EN0_RSARHI);
	outb_p(E8390_RREAD | E8390_START, ioaddr+E8390_CMD);
	insw(ioaddr+NE_DATAPORT, buf, len>>1);
}
static void __init ne2k_cbus_writemem(struct net_device *dev, int ioaddr, unsigned short memaddr, const char *buf, unsigned short len)
{
	struct ei_device *ei_local = (struct ei_device *)(dev->priv);
	outb_p(E8390_NODMA | E8390_START, ioaddr+E8390_CMD);
	outb_p(                ENISR_RDC, ioaddr+EN0_ISR);
	outb_p(              len & 0xff , ioaddr+EN0_RCNTLO);
	outb_p(              len >> 8   , ioaddr+EN0_RCNTHI);
	outb_p(          memaddr & 0xff , ioaddr+EN0_RSARLO);
	outb_p(          memaddr >> 8   , ioaddr+EN0_RSARHI);
	outb_p(E8390_RWRITE | E8390_START, ioaddr+E8390_CMD);
	outsw(ioaddr+NE_DATAPORT, buf, len>>1);
}
#endif

/* End of ne2k_cbus.h */
