/*
 * Copyright 1994-2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _SYS_DMFE_IMPL_H
#define	_SYS_DMFE_IMPL_H

#pragma ident	"@(#)dmfe_impl.h	1.4	03/06/17 SMI"

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/stat.h>
#include <sys/pci.h>
#include <sys/note.h>
#include <sys/modctl.h>
#include <sys/kstat.h>
#include <sys/ethernet.h>
#include <sys/dlpi.h>
#include <sys/devops.h>
#include <sys/debug.h>
#include <sys/cyclic.h>
#include <sys/conf.h>

#include <inet/common.h>
#include <inet/nd.h>
#include <inet/mi.h>

#include <sys/dditypes.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#include <sys/gld.h>

#include <sys/miiregs.h>
#include <sys/dmfe.h>

#define	DRIVER_NAME		"dmfe"

/*
 * Describes the identity of a specific chip
 */
typedef struct {
	uint16_t		vendor;
	uint16_t		device;
	uint8_t			revision;
	uint8_t			spare;
} chip_id_t;

/*
 * Describes the state of a descriptor ring
 *
 * NOTE: n_free and next_busy are only used for the Tx descriptors
 * and are not valid on the receive side.
 */
typedef struct {
	uint32_t		n_desc;		/* # of descriptors	    */
	uint32_t		n_free;		/* # of free descriptors    */
	uint32_t		next_free;	/* next index to use/check  */
	uint32_t		next_busy;	/* next index to reclaim    */
} desc_state_t;

/*
 * Describes one chunk of allocated DMA-able memory
 */
typedef struct {
	ddi_dma_handle_t	dma_hdl;
	ddi_acc_handle_t	acc_hdl;
	size_t			alength;	/* allocated size	*/
	caddr_t			mem_va;		/* CPU VA of memory	*/
	uint32_t		spare1;
	uint32_t		mem_dvma;	/* DVMA addr of memory	*/
	caddr_t			setup_va;
	uint32_t		spare2;
	uint32_t		setup_dvma;
	int			spare3;
	int			ncookies;
} dma_area_t;

/*
 * Named Data (ND) Parameter Management Structure
 */
typedef struct {
	uint32_t		ndp_info;
	uint32_t		ndp_min;
	uint32_t		ndp_max;
	uint32_t		ndp_val;
	char			*ndp_name;
} nd_param_t;

/*
 * NDD parameter indexes, divided into:
 *
 *	read-only parameters describing the link state
 *	read-write parameters controlling the advertised link capabilities
 *	read-only parameters describing the device link capabilities
 *	read-only parameters describing the link-partner's link capabilities
 */
enum {
	PARAM_LINK_STATUS,
	PARAM_LINK_SPEED,
	PARAM_LINK_MODE,

	PARAM_ADV_AUTONEG_CAP,
	PARAM_ADV_100T4_CAP,
	PARAM_ADV_100FDX_CAP,
	PARAM_ADV_100HDX_CAP,
	PARAM_ADV_10FDX_CAP,
	PARAM_ADV_10HDX_CAP,

	PARAM_BMSR_AUTONEG_CAP,
	PARAM_BMSR_100T4_CAP,
	PARAM_BMSR_100FDX_CAP,
	PARAM_BMSR_100HDX_CAP,
	PARAM_BMSR_10FDX_CAP,
	PARAM_BMSR_10HDX_CAP,

	PARAM_LP_AUTONEG_CAP,
	PARAM_LP_100T4_CAP,
	PARAM_LP_100FDX_CAP,
	PARAM_LP_100HDX_CAP,
	PARAM_LP_10FDX_CAP,
	PARAM_LP_10HDX_CAP,

	PARAM_COUNT
};

/*
 * Kstat indexes, divided into:
 *
 *	cyclic activity
 *	reasons for waking the factotum
 *	the factotum's activities
 *	link state updates
 *	MII-level register values
 */
enum {
	KS_CYCLIC_RUN,

	KS_TICK_LINK_STATE,
	KS_TICK_LINK_POLL,
	KS_LINK_INTERRUPT,
	KS_TX_STALL,
	KS_CHIP_ERROR,

	KS_FACTOTUM_RUN,
	KS_RECOVERY,
	KS_LINK_CHECK,

	KS_LINK_UP,
	KS_LINK_UP_CNT,
	KS_LINK_DROP_CNT,
	KS_LINK_CYCLE_UP_CNT,
	KS_LINK_CYCLE_DOWN_CNT,

	KS_MII_BMSR,
	KS_MII_ANAR,
	KS_MII_ANLPAR,
	KS_MII_ANER,
	KS_MII_DSCSR,

	KS_COUNT
};

/*
 * Actual state of the DM9102A chip
 */
enum chip_state {
	CHIP_ERROR = -1,			/* error, need reset	*/
	CHIP_UNKNOWN,				/* Initial state only	*/
	CHIP_RESET,				/* reset, need init	*/
	CHIP_STOPPED,				/* Tx/Rx stopped	*/
	CHIP_TX_ONLY,				/* Tx (re)started	*/
	CHIP_TX_RX,				/* Tx & Rx (re)started	*/
	CHIP_RUNNING				/* with interrupts	*/
};

/*
 * Required state according to GLD
 */
enum gld_state {
	GLD_UNKNOWN,
	GLD_RESET,
	GLD_STOPPED,
	GLD_STARTED
};

/*
 * Current state of the physical link
 *
 *	DOWN		No link established.
 *	UNKNOWN		Initial state, also indicates "autonegotiating".
 *	UP		Link established, commuication should be possible.
 */
typedef enum {
	LINK_DOWN = -1,
	LINK_UNKNOWN = 0,
	LINK_UP
} link_state_t;

/*
 * (Internal) return values from ioctl subroutines
 */
enum ioc_reply {
	IOC_INVAL = -1,				/* bad, NAK with EINVAL	*/
	IOC_DONE,				/* OK, reply sent	*/
	IOC_REPLY,				/* OK, just send reply	*/
	IOC_ACK,				/* OK, just send ACK	*/
	IOC_RESTART,				/* OK, restart & reply	*/
	IOC_RESTART_ACK				/* OK, restart & ACK	*/
};

/*
 * Per-instance soft-state structure
 */
typedef struct {
	/*
	 * These fields are set by attach() and unchanged thereafter ...
	 */
	dev_info_t		*devinfo;	/* device instance	*/
	gld_mac_info_t		*macinfo;	/* GLD instance data	*/
	ddi_acc_handle_t	io_handle;	/* DDI I/O handle	*/
	caddr_t			io_reg;		/* mapped registers	*/

	uint32_t		debug;		/* per-instance debug	*/
	uint32_t		progress;	/* attach tracking	*/
	chip_id_t		chipid;
	uint8_t			vendor_addr[ETHERADDRL];
	char			ifname[12];	/* "dmfeXXXX"		*/

	dma_area_t		tx_desc;	/* transmit descriptors	*/
	dma_area_t		tx_buff;	/* transmit buffers	*/
	dma_area_t		rx_desc;	/* receive descriptors	*/
	dma_area_t		rx_buff;	/* receive buffers	*/

	cyclic_id_t		cycid;		/* cyclic callback id	 */
	ddi_softintr_t		factotum_id;	/* identity of factotum	*/
	ddi_iblock_cookie_t	iblk;

	/*
	 * Locks:
	 *
	 * <milock> is used only by the MII (PHY) level code, to ensure
	 *	exclusive access during the bit-twiddling needed to send
	 *	signals along the MII serial bus.  These operations are
	 *	--S--L--O--W-- so we keep this lock separate, so that
	 *	faster operations (e.g. interrupts) aren't delayed by
	 *	waiting for it.
	 *
	 * <oplock> is a general "outer" lock, protecting most r/w data
	 *	and chip state.  It is also acquired by the interrupt
	 *	handler.
	 *
	 * <rxlock> is used to protect the Rx-side buffers, descriptors,
	 *	and statistics during a single call to dmfe_getp().
	 *	This is called from inside the interrupt handler, but
	 *	<oplock> is not held across this call.
	 *
	 * <txlock> is an "inner" lock, and protects only the Tx-side
	 *	data below and in the ring buffers/descriptors.  The
	 *	Tx-side code uses only this lock, avoiding contention
	 *	with the receive-side code.
	 *
	 * Any of the locks can be acquired singly, but where multiple
	 * locks are acquired, they *must* be in the order:
	 *
	 *	milock >>> oplock >>> rxlock >>> txlock.
	 *
	 * *None* of these locks may be held across calls out to the
	 * GLD routines gld_recv() or gld_sched(); GLD's <maclock> must
	 * be regarded as an *outermost* lock in all cases, as it will
	 * already be held before calling the ioctl() or get_stats()
	 * entry points - which then have to acquire multiple locks, in
	 * the order described here.
	 */
	kmutex_t		milock[1];
	kmutex_t		oplock[1];
	kmutex_t		rxlock[1];
	kmutex_t		txlock[1];

	/*
	 * DMFE Extended kstats, protected by <oplock>
	 */
	kstat_t			*ksp;
	kstat_named_t		*knp;

	/*
	 * GLD statistics; the prefix tells which lock each is protected by.
	 */
	uint64_t		op_stats_speed;
	uint32_t		op_stats_duplex;
	uint32_t		op_stats_media;
	uint32_t		op_stats_intr;

	uint32_t		rx_stats_errrcv;
	uint32_t		rx_stats_overflow;
	uint32_t		rx_stats_short;
	uint32_t		rx_stats_crc;
	uint32_t		rx_stats_frame_too_long;
	uint32_t		rx_stats_mac_rcv_error;
	uint32_t		rx_stats_frame;
	uint32_t		rx_stats_missed;
	uint32_t		rx_stats_norcvbuf;

	uint32_t		tx_stats_errxmt;
	uint32_t		tx_stats_mac_xmt_error;
	uint32_t		tx_stats_underflow;
	uint32_t		tx_stats_xmtlatecoll;
	uint32_t		tx_stats_nocarrier;
	uint32_t		tx_stats_excoll;
	uint32_t		tx_stats_first_coll;
	uint32_t		tx_stats_multi_coll;
	uint32_t		tx_stats_collisions;
	uint32_t		tx_stats_defer;

	/*
	 * These two sets of desciptors are manipulated during
	 * packet receive/transmit respectively.
	 */
	desc_state_t		rx;		/* describes Rx ring	*/
	desc_state_t		tx;		/* describes Tx ring	*/

	/*
	 * Miscellaneous Tx-side variables (protected by txlock)
	 */
	uint32_t		tx_pending_tix;	/* tix since reclaim	*/

	/*
	 * Miscellaneous operating variables (protected by oplock)
	 */
	uint32_t		link_poll_tix;	/* tix until link poll	 */
	uint16_t		factotum_flag;	/* callback pending	 */
	uint16_t		need_setup;	/* send-setup pending	 */
	uint32_t		opmode;		/* operating mode shadow */
	uint32_t		imask;		/* interrupt mask shadow */
	enum gld_state		gld_state;	/* RESET/STOPPED/STARTED */
	enum chip_state		chip_state;	/* see above		 */

	/*
	 * Physical link state data (protected by oplock)
	 */
	link_state_t		link_state;	/* See above		*/
	const char		*link_down_msg;	/* reason for link DOWN	*/
	const char		*link_up_msg;	/* comment on link UP	*/

	/*
	 * PHYceiver state data (protected by milock)
	 */
	int			phy_addr;	/* should be 1!		*/
	uint16_t		phy_control;	/* last value written	*/
	uint16_t		phy_anar_w;	/* last value written	*/
	uint16_t		phy_anar_r;	/* latest value read	*/
	uint16_t		phy_anlpar;	/* latest value read	*/
	uint16_t		phy_aner;
	uint16_t		phy_dscsr;	/* latest value read	*/
	uint16_t		phy_bmsr;	/* latest value read	*/
	uint16_t		rsvd;		/* reserved for future use */
	uint32_t		phy_bmsr_lbolt;	/* time of BMSR change	*/

	/*
	 * Current Ethernet address & multicast map ...
	 */
	uint8_t			curr_addr[ETHERADDRL];
	uint8_t			mcast_refs[MCASTBUF_SIZE];
	boolean_t		addr_set;

	/*
	 * NDD parameters
	 */
	caddr_t			nd_data_p;
	nd_param_t		nd_params[PARAM_COUNT];

	/*
	 * Guard element used to check data integrity
	 */
	uint64_t		dmfe_guard;
} dmfe_t;

/*
 * 'Progress' bit flags ...
 */
#define	PROGRESS_CONFIG		0x0001	/* config space initialised	*/
#define	PROGRESS_NDD		0x0002	/* NDD parameters set up	*/
#define	PROGRESS_REGS		0x0004	/* registers mapped		*/
#define	PROGRESS_BUFS		0x0008	/* buffers allocated		*/
#define	PROGRESS_SOFTINT	0x0010	/* softint registered		*/
#define	PROGRESS_HWINT		0x0020	/* h/w interrupt registered	*/
#define	PROGRESS_KSTATS		0x0040	/* kstats created		*/

/*
 * Shorthand for the NDD parameters
 */
#define	param_linkup		nd_params[PARAM_LINK_STATUS].ndp_val
#define	param_speed		nd_params[PARAM_LINK_SPEED].ndp_val
#define	param_duplex		nd_params[PARAM_LINK_MODE].ndp_val
#define	param_autoneg		nd_params[PARAM_ADV_AUTONEG_CAP].ndp_val
#define	param_anar_100T4	nd_params[PARAM_ADV_100T4_CAP].ndp_val
#define	param_anar_100fdx	nd_params[PARAM_ADV_100FDX_CAP].ndp_val
#define	param_anar_100hdx	nd_params[PARAM_ADV_100HDX_CAP].ndp_val
#define	param_anar_10fdx	nd_params[PARAM_ADV_10FDX_CAP].ndp_val
#define	param_anar_10hdx	nd_params[PARAM_ADV_10HDX_CAP].ndp_val
#define	param_bmsr_autoneg	nd_params[PARAM_BMSR_AUTONEG_CAP].ndp_val
#define	param_bmsr_100T4	nd_params[PARAM_BMSR_100T4_CAP].ndp_val
#define	param_bmsr_100fdx	nd_params[PARAM_BMSR_100FDX_CAP].ndp_val
#define	param_bmsr_100hdx	nd_params[PARAM_BMSR_100HDX_CAP].ndp_val
#define	param_bmsr_10fdx	nd_params[PARAM_BMSR_10FDX_CAP].ndp_val
#define	param_bmsr_10hdx	nd_params[PARAM_BMSR_10HDX_CAP].ndp_val
#define	param_lp_autoneg	nd_params[PARAM_LP_AUTONEG_CAP].ndp_val
#define	param_lp_100T4		nd_params[PARAM_LP_100T4_CAP].ndp_val
#define	param_lp_100fdx		nd_params[PARAM_LP_100FDX_CAP].ndp_val
#define	param_lp_100hdx		nd_params[PARAM_LP_100HDX_CAP].ndp_val
#define	param_lp_10fdx		nd_params[PARAM_LP_10FDX_CAP].ndp_val
#define	param_lp_10hdx		nd_params[PARAM_LP_10HDX_CAP].ndp_val

/*
 * Derive DMFE state from GLD's macinfo or vice versa
 */
#define	DMFE_STATE(macinfo)	((dmfe_t *)((macinfo)->gldm_private))
#define	DMFE_MACINFO(dmfep)	((dmfep)->macinfo)

/*
 * Sync a DMA area described by a dma_area_t
 */
#define	DMA_SYNC(descp, flag)	((void) ddi_dma_sync((descp)->dma_hdl,	\
					0, (descp)->alength, flag))

/*
 * Next value of a cyclic index
 */
#define	NEXT(index, limit)	((index)+1 < (limit) ? (index)+1 : 0);

/*
 * Copy an ethernet address
 */
#define	ethaddr_copy(src, dst)	bcopy((src), (dst), ETHERADDRL)

/*
 * Get/set/increment a kstat value
 */
#define	KS_GET(knp, id)		((knp) ? (knp)[id].value.ui64 : 0)

#define	KS_SET(knp, id, val)	do {					\
				    if (knp)				\
					(knp)[id].value.ui64 = (val);	\
				    _NOTE(CONSTANTCONDITION)		\
				} while (0)

#define	KS_INC(knp, id)		do {					\
				    if (knp)				\
					(knp)[id].value.ui64 += 1;	\
				    _NOTE(CONSTANTCONDITION)		\
				} while (0)

#define	DMFE_GUARD		0x1919603003090218

/*
 * 'Debug' bit flags ...
 */
#define	DMFE_DBG_TRACE		0x0001		/* general flow tracing	*/
#define	DMFE_DBG_REGS		0x0002		/* low-level accesses	*/
#define	DMFE_DBG_RECV		0x0004		/* receive-side code	*/
#define	DMFE_DBG_SEND		0x0008		/* packet-send code	*/
#define	DMFE_DBG_ADDR		0x0010		/* address-setting code	*/
#define	DMFE_DBG_GLD		0x0020		/* GLD entry points	*/
#define	DMFE_DBG_FACT		0x0040		/* factotum (softint)	*/
#define	DMFE_DBG_TICK		0x0080		/* GPT ticker		*/
#define	DMFE_DBG_INT		0x0100		/* interrupt handler	*/
#define	DMFE_DBG_STATS		0x0200		/* statistics		*/
#define	DMFE_DBG_IOCTL		0x0400		/* ioctl/loopback code	*/
#define	DMFE_DBG_INIT		0x0800		/* initialisation	*/
#define	DMFE_DBG_MII		0x1000		/* low-level MII/PHY	*/
#define	DMFE_DBG_LINK		0x2000		/* Link status check	*/
#define	DMFE_DBG_NDD		0x4000		/* NDD parameters	*/

/*
 * Debugging ...
 */
#ifdef	DEBUG
#define	DMFEDEBUG		1
#else
#define	DMFEDEBUG		0
#endif

#if	DMFEDEBUG

extern uint32_t dmfe_debug;
extern void (*dmfe_gdb())(const char *fmt, ...);
extern void (*dmfe_db(dmfe_t *dmfep))(const char *fmt, ...);

/*
 * Define DMFE_DBG to be the relevant flag from the set above before
 * using the DMFE_GDEBUG() or DMFE_DEBUG() macros.  The 'G' versions
 * look at the Global debug flag word (dmfe_debug); the non-G versions
 * look in the per-instance data (dmfep->debug) and so require a variable
 * called 'dmfep' to be in scope (and initialised!)
 *
 * You could redefine DMFE_TRC too if you really need two different
 * flavours of debugging output in the same area of code, but I don't
 * really recommend it.
 */

#define	DMFE_TRC		DMFE_DBG_TRACE	/* default 'trace' bit	*/

#define	DMFE_GDEBUG(args)	do {					\
					if (dmfe_debug & (DMFE_DBG))	\
						(*dmfe_gdb()) args;	\
					_NOTE(CONSTANTCONDITION)	\
				} while (0)

#define	DMFE_GTRACE(args)	do {					\
					if (dmfe_debug & (DMFE_TRC))	\
						(*dmfe_gdb()) args;	\
					_NOTE(CONSTANTCONDITION)	\
				} while (0)

#define	DMFE_DEBUG(args)	do {					\
					if (dmfep->debug & (DMFE_DBG))	\
						(*dmfe_db(dmfep)) args;	\
					_NOTE(CONSTANTCONDITION)	\
				} while (0)

#define	DMFE_TRACE(args)	do {					\
					if (dmfep->debug & (DMFE_TRC))	\
						(*dmfe_db(dmfep)) args;	\
					_NOTE(CONSTANTCONDITION)	\
				} while (0)

#else

#define	DMFE_DEBUG(args)	do ; _NOTE(CONSTANTCONDITION) while (0)
#define	DMFE_TRACE(args)	do ; _NOTE(CONSTANTCONDITION) while (0)
#define	DMFE_GDEBUG(args)	do ; _NOTE(CONSTANTCONDITION) while (0)
#define	DMFE_GTRACE(args)	do ; _NOTE(CONSTANTCONDITION) while (0)

#endif	/* DMFEDEBUG */


/*
 * Inter-source-file linkage ...
 */

/* dmfe_log.c */
void dmfe_warning(dmfe_t *dmfep, const char *fmt, ...);
void dmfe_error(dmfe_t *dmfep, const char *fmt, ...);
void dmfe_notice(dmfe_t *dmfep, const char *fmt, ...);
void dmfe_log(dmfe_t *dmfep, const char *fmt, ...);
void dmfe_log_init(void);
void dmfe_log_fini(void);

/* dmfe_main.c */
uint32_t dmfe_chip_get32(dmfe_t *dmfep, off_t offset);
void dmfe_chip_put32(dmfe_t *dmfep, off_t offset, uint32_t value);

/* dmfe_mii.c */
boolean_t dmfe_init_phy(dmfe_t *dmfep);
void dmfe_update_phy(dmfe_t *dmfep);
boolean_t dmfe_check_link(dmfe_t *dmfep);
void dmfe_recheck_link(dmfe_t *dmfep, boolean_t ioctl);

/* dmfe_ndd.c */
int dmfe_nd_init(dmfe_t *dmfep);
enum ioc_reply dmfe_nd_ioctl(dmfe_t *dmfep, queue_t *wq, mblk_t *mp, int cmd);
void dmfe_nd_cleanup(dmfe_t *dmfep);

#ifdef __cplusplus
}
#endif

#endif	/* _SYS_DMFE_IMPL_H */
