/*-
 * Copyright (c) 2003
 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
 * 	All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Author: Hartmut Brandt <harti@freebsd.org>
 *
 * $FreeBSD: src/sys/dev/patm/if_patmvar.h,v 1.5 2005/06/10 16:49:13 brooks Exp $
 *
 * Driver for IDT77252 (ABR) based cards like ProSum's.
 */

/* legal values are 0, 1, 2 and 8 */
#define	PATM_VPI_BITS	2
#define	PATM_CFG_VPI	IDT_CFG_VP2

/* receive status queue size */
#define	PATM_RSQ_SIZE		512
#define	PATM_CFQ_RSQ_SIZE	IDT_CFG_RXQ512	

/* alignment for SQ memory */
#define	PATM_SQ_ALIGNMENT	8192

#define	PATM_PROATM_NAME_OFFSET	060
#define	PATM_PROATM_NAME	"PROATM"
#define	PATM_PROATM_MAC_OFFSET	044
#define	PATM_IDT_MAC_OFFSET	0154

/* maximum number of packets on UBR queue */
#define	PATM_DLFT_MAXQ		1000

/* maximum number of packets on other queues. This should depend on the
 * traffic contract. */
#define	PATM_TX_IFQLEN		100

/*
 * Maximum number of DMA maps we allocate. This is the minimum that can be
 * set larger via a sysctl.
 * Starting number of DMA maps.
 * Step for growing.
 */
#define	PATM_CFG_TXMAPS_MAX	1024
#define	PATM_CFG_TXMAPS_INIT	128
#define	PATM_CFG_TXMAPS_STEP	128

/* percents of TST slots to keep for non-CBR traffic */
#define	PATM_TST_RESERVE	2

/*
 * Structure to hold TX DMA maps
 */
struct patm_txmap {
	SLIST_ENTRY(patm_txmap) link;
	bus_dmamap_t	map;
};

/*
 * Receive buffers.
 *
 * We manage our own external mbufs for small receive buffers for two reasons:
 * the card may consume a rather large number of buffers. Mapping each buffer
 * would consume a lot of iospace on sparc64. Also the card allows us to set
 * a 32-bit handle for identification of the buffers. On a 64-bit system this
 * requires us to use a mapping between buffers and handles.
 *
 * For large buffers we use mbuf clusters directly. We track these by using
 * an array of pointers (lbufs) to special structs and a free list of these
 * structs.
 *
 * For AAL0 cell we use FBQ2 and make the 1 cell long.
 */
/*
 * Define the small buffer chunk so that we have at least 16 byte free
 * at the end of the chunk and that there is an integral number of chunks
 * in a page.
 */
#define	SMBUF_PAGE_SIZE		16384	/* 16k pages */
#define	SMBUF_MAX_PAGES		64	/* maximum number of pages */
#define	SMBUF_CHUNK_SIZE	256	/* 256 bytes per chunk */
#define	SMBUF_CELLS		5
#define	SMBUF_SIZE		(SMBUF_CELLS * 48)
#define	SMBUF_THRESHOLD		9	/* 9/16 of queue size */
#define	SMBUF_NI_THRESH		3
#define	SMBUF_CI_THRESH		1

#define	VMBUF_PAGE_SIZE		16384	/* 16k pages */
#define	VMBUF_MAX_PAGES		16	/* maximum number of pages */
#define	VMBUF_CHUNK_SIZE	64	/* 64 bytes per chunk */
#define	VMBUF_CELLS		1
#define	VMBUF_SIZE		(VMBUF_CELLS * 48)
#define	VMBUF_THRESHOLD		15	/* 15/16 of size */

#define	SMBUF_OFFSET	(SMBUF_CHUNK_SIZE - 8 - SMBUF_SIZE)
#define	VMBUF_OFFSET	0

#define	MBUF_SHANDLE	0x00000000
#define	MBUF_LHANDLE	0x80000000
#define	MBUF_VHANDLE	0x40000000
#define	MBUF_HMASK	0x3fffffff

/*
 * Large buffers
 *
 * The problem with these is the maximum count. When the card assembles
 * a AAL5 pdu it moves a buffer from the FBQ to the VC. This frees space
 * in the FBQ, put the buffer may pend on the card for an unlimited amount
 * of time (we don't idle connections). This means that the upper limit
 * on buffers on the card may be (no-of-open-vcs + FBQ_SIZE). Because
 * this is far too much, make this a tuneable. We could also make
 * this dynamic by allocating pages of several lbufs at once during run time.
 */
#define	LMBUF_MAX		(IDT_FBQ_SIZE * 2)
#define	LMBUF_CELLS		(MCLBYTES / 48)	/* 42 cells = 2048 byte */
#define	LMBUF_SIZE		(LMBUF_CELLS * 48)
#define	LMBUF_THRESHOLD		9	/* 9/16 of queue size */
#define	LMBUF_OFFSET		(MCLBYTES - LMBUF_SIZE)
#define	LMBUF_NI_THRESH		3
#define	LMBUF_CI_THRESH		1

#define	LMBUF_HANDLE		0x80000000

struct lmbuf {
	SLIST_ENTRY(lmbuf)	link;	/* free list link */
	bus_dmamap_t		map;	/* DMA map */
	u_int			handle;	/* this is the handle index */
	struct mbuf		*m;	/* the current mbuf */
	bus_addr_t		phy;	/* phy addr */
};

#define	PATM_CID(SC, VPI, VCI)	\
    (((VPI) << IFP2IFATM((SC)->ifp)->mib.vci_bits) | (VCI))

/*
 * Internal driver statistics
 */
struct patm_stats {
	uint32_t	raw_cells;
	uint32_t	raw_no_vcc;
	uint32_t	raw_no_buf;
	uint32_t	tx_qfull;
	uint32_t	tx_out_of_tbds;
	uint32_t	tx_out_of_maps;
	uint32_t	tx_load_err;
};

/*
 * These are allocated as DMA able memory
 */
struct patm_scd {
	struct idt_tbd	scq[IDT_SCQ_SIZE];
	LIST_ENTRY(patm_scd) link;	/* all active SCDs */
	uint32_t	sram;		/* SRAM address */
	bus_addr_t	phy;		/* physical address */
	bus_dmamap_t	map;		/* DMA map */
	u_int		tail;		/* next free entry for host */
	int		space;		/* number of free entries (minus one) */
	u_int		slots;		/* CBR slots allocated */
	uint8_t		tag;		/* next tag for TSI */
	uint8_t		last_tag;	/* last tag checked in interrupt */
	uint8_t		num_on_card;	/* number of PDUs on tx queue */
	uint8_t		lacr;		/* LogACR value */
	uint8_t		init_er;	/* LogER value */
	struct ifqueue	q;		/* queue of packets */
	struct mbuf	*on_card[IDT_TSQE_TAG_SPACE];
};

/*
 * Per-VCC data
 */
struct patm_vcc {
	struct atmio_vcc vcc;		/* caller's parameters */
	void		*rxhand;	/* NATM handle */
	u_int		vflags;		/* open and other flags */
	uint32_t	ipackets;	/* packets received */
	uint32_t	opackets;	/* packets sent */
	uint64_t	ibytes;		/* bytes received */
	uint64_t	obytes;		/* bytes sent */

	struct mbuf	*chain;		/* currently received chain */
	struct mbuf	*last;		/* end of chain */
	u_int		cid;		/* index */
	u_int		cps;		/* last ABR cps */
	struct patm_scd	*scd;
};
#define	PATM_VCC_TX_OPEN	0x0001
#define	PATM_VCC_RX_OPEN	0x0002
#define	PATM_VCC_TX_CLOSING	0x0004
#define	PATM_VCC_RX_CLOSING	0x0008
#define	PATM_VCC_OPEN		0x000f	/* all the above */

#define	PATM_RAW_CELL		0x0000	/* 53 byte cells */
#define	PATM_RAW_NOHEC		0x0100	/* 52 byte cells */
#define	PATM_RAW_CS		0x0200	/* 64 byte cell stream */
#define	PATM_RAW_FORMAT		0x0300	/* format mask */

/*
 * Per adapter data
 */
struct patm_softc {
	struct ifnet		*ifp;		/* common ATM stuff */
	struct mtx		mtx;		/* lock */
	struct ifmedia		media;		/* media */
	device_t		dev;		/* device */
	struct resource *	memres;		/* memory resource */
	bus_space_handle_t	memh;		/* handle */
	bus_space_tag_t		memt;		/* ... and tag */
	int			irqid;		/* resource id */
	struct resource *	irqres;		/* resource */
	void *			ih;		/* interrupt handle */
	struct utopia		utopia;		/* phy state */
	const struct idt_mmap	*mmap;		/* SRAM memory map */
	u_int			flags;		/* see below */
	u_int			revision;	/* chip revision */

	/* DMAable status queue memory */
	size_t			sq_size;	/* size of memory area */
	bus_dma_tag_t		sq_tag;		/* DMA tag */
	bus_dmamap_t		sq_map;		/* map */

	bus_addr_t		tsq_phy;	/* phys addr. */
	struct idt_tsqe		*tsq;		/* transmit status queue */
	struct idt_tsqe		*tsq_next;	/* last processed entry */
	struct idt_rsqe		*rsq;		/* receive status queue */
	bus_addr_t		rsq_phy;	/* phys addr. */
	u_int			rsq_last;	/* last processed entry */
	struct idt_rawhnd	*rawhnd;	/* raw cell handle */
	bus_addr_t		rawhnd_phy;	/* phys addr. */

	/* TST */
	u_int			tst_state;	/* active TST and others */
	u_int			tst_jump[2];	/* address of the jumps */
	u_int			tst_base[2];	/* base address of TST */
	u_int			*tst_soft;	/* soft TST */
	struct mtx		tst_lock;
	struct callout		tst_callout;
	u_int			tst_free;	/* free slots */
	u_int			tst_reserve;	/* non-CBR reserve */
	u_int			bwrem;		/* remaining bandwith */

	/* sysctl support */
	struct sysctl_ctx_list	sysctl_ctx;
	struct sysctl_oid	*sysctl_tree;

	/* EEPROM contents */
	uint8_t			eeprom[256];

	/* large buffer mapping */
	bus_dma_tag_t		lbuf_tag;	/* DMA tag */
	u_int			lbuf_max;	/* maximum number */
	struct lmbuf		*lbufs;		/* array for indexing */
	SLIST_HEAD(,lmbuf)	lbuf_free_list;	/* free list */

	/* small buffer handling */
	bus_dma_tag_t		sbuf_tag;	/* DMA tag */
	struct mbpool		*sbuf_pool;	/* pool */
	struct mbpool		*vbuf_pool;	/* pool */

	/* raw cell queue */
	struct lmbuf		*rawh;		/* current header buf */
	u_int			rawi;		/* cell index into buffer */

	/* statistics */
	struct patm_stats	stats;		/* statistics */

	/* Vccs */
	struct patm_vcc		**vccs;		/* channel pointer array */
	u_int			vccs_open;	/* number of open channels */
	uma_zone_t		vcc_zone;
	struct cv		vcc_cv;

	/* SCDs */
	uint32_t		scd_free;	/* SRAM of first free SCD */
	bus_dma_tag_t		scd_tag;
	struct patm_scd		*scd0;
	LIST_HEAD(, patm_scd)	scd_list;	/* list of all active SCDs */

	/* Tx */
	bus_dma_tag_t		tx_tag;		/* for transmission */
	SLIST_HEAD(, patm_txmap) tx_maps_free;	/* free maps */
	u_int			tx_nmaps;	/* allocated maps */
	u_int			tx_maxmaps;	/* maximum number */
	struct uma_zone		*tx_mapzone;	/* zone for maps */

#ifdef PATM_DEBUG
	/* debugging */
	u_int			debug;
#endif
};

/* flags */
#define	PATM_25M	0x0001		/* 25MBit card */
#define	PATM_SBUFW	0x0002		/* warned */
#define	PATM_VBUFW	0x0004		/* warned */
#define	PATM_UNASS	0x0010		/* unassigned cells */

#define	PATM_CLR	0x0007		/* clear on stop */

/* tst - uses unused fields */
#define	TST_BOTH	0x03000000
#define	TST_CH0		0x01000000
#define	TST_CH1		0x02000000
/* tst_state */
#define	TST_ACT1	0x0001		/* active TST */
#define	TST_PENDING	0x0002		/* need update */
#define	TST_WAIT	0x0004		/* wait fo jump */

#define	patm_printf(SC, ...)	if_printf((SC)->ifp, __VA_ARGS__);

#ifdef PATM_DEBUG
/*
 * Debugging
 */
enum {
	DBG_ATTACH	= 0x0001,	/* attaching the card */
	DBG_INTR	= 0x0002,	/* interrupts */
	DBG_REG		= 0x0004,	/* register access */
	DBG_SRAM	= 0x0008,	/* SRAM access */
	DBG_PHY		= 0x0010,	/* PHY access */
	DBG_IOCTL	= 0x0020,	/* ioctl */
	DBG_FREEQ	= 0x0040,	/* free bufq supply */
	DBG_VCC		= 0x0080,	/* open/close */
	DBG_TX		= 0x0100,	/* transmission */
	DBG_TST		= 0x0200,	/* TST */

	DBG_ALL		= 0xffff
};

#define	patm_debug(SC, FLAG, ...) do {					\
	if((SC)->debug & DBG_##FLAG) { 					\
		if_printf((SC)->ifp, "%s: ", __func__);	\
		printf(__VA_ARGS__);					\
		printf("\n");						\
	}								\
    } while (0)
#else

#define patm_debug(SC, FLAG, ...) do { } while (0)

#endif

/* start output */
void patm_start(struct ifnet *);

/* ioctl handler */
int patm_ioctl(struct ifnet *, u_long, caddr_t);

/* start the interface */
void patm_init(void *);

/* start the interface with the lock held */
void patm_initialize(struct patm_softc *);

/* stop the interface */
void patm_stop(struct patm_softc *);

/* software reset of interface */
void patm_reset(struct patm_softc *);

/* interrupt handler */
void patm_intr(void *);

/* check RSQ */
void patm_intr_rsq(struct patm_softc *sc);

/* enable the vcc */
void patm_load_vc(struct patm_softc *sc, struct patm_vcc *vcc, int reload);

/* close the given vcc for transmission */
void patm_tx_vcc_close(struct patm_softc *, struct patm_vcc *);

/* close the given vcc for receive */
void patm_rx_vcc_close(struct patm_softc *, struct patm_vcc *);

/* transmission side finally closed */
void patm_tx_vcc_closed(struct patm_softc *, struct patm_vcc *);

/* receive side finally closed */
void patm_rx_vcc_closed(struct patm_softc *, struct patm_vcc *);

/* vcc closed */
void patm_vcc_closed(struct patm_softc *, struct patm_vcc *);

/* check if we can open this one */
int patm_tx_vcc_can_open(struct patm_softc *, struct patm_vcc *);

/* check if we can open this one */
int patm_rx_vcc_can_open(struct patm_softc *, struct patm_vcc *);

/* open it */
void patm_tx_vcc_open(struct patm_softc *, struct patm_vcc *);

/* open it */
void patm_rx_vcc_open(struct patm_softc *, struct patm_vcc *);

/* receive packet */
void patm_rx(struct patm_softc *, struct idt_rsqe *);

/* packet transmitted */
void patm_tx(struct patm_softc *, u_int, u_int);

/* VBR connection went idle */
void patm_tx_idle(struct patm_softc *, u_int);

/* allocate an SCQ */
struct patm_scd *patm_scd_alloc(struct patm_softc *);

/* free an SCD */
void patm_scd_free(struct patm_softc *sc, struct patm_scd *scd);

/* setup SCD in SRAM */
void patm_scd_setup(struct patm_softc *sc, struct patm_scd *scd);

/* setup TCT entry in SRAM */
void patm_tct_setup(struct patm_softc *, struct patm_scd *, struct patm_vcc *);

/* free a large buffer */
void patm_lbuf_free(struct patm_softc *sc, struct lmbuf *b);

/* Process the raw cell at the given address */
void patm_rx_raw(struct patm_softc *sc, u_char *cell);

/* load a one segment DMA map */
void patm_load_callback(void *, bus_dma_segment_t *, int, int);

/* network operation register access */
static __inline uint32_t
patm_nor_read(struct patm_softc *sc, u_int reg)
{
	uint32_t val;

	val = bus_space_read_4(sc->memt, sc->memh, reg);
	patm_debug(sc, REG, "reg(0x%x)=%04x", reg, val);
	return (val);
}
static __inline void
patm_nor_write(struct patm_softc *sc, u_int reg, uint32_t val)
{

	patm_debug(sc, REG, "reg(0x%x)=%04x", reg, val);
	bus_space_write_4(sc->memt, sc->memh, reg, val);
}

/* Execute command */
static __inline void
patm_cmd_wait(struct patm_softc *sc)
{
	while (patm_nor_read(sc, IDT_NOR_STAT) & IDT_STAT_CMDBZ)
		;
}
static __inline void
patm_cmd_exec(struct patm_softc *sc, uint32_t cmd)
{
	patm_cmd_wait(sc);
	patm_nor_write(sc, IDT_NOR_CMD, cmd);
}

/* Read/write SRAM at the given word address. */
static __inline uint32_t
patm_sram_read(struct patm_softc *sc, u_int addr)
{
	uint32_t val;

	patm_cmd_exec(sc, IDT_MKCMD_RSRAM(addr));
	patm_cmd_wait(sc);
	val = patm_nor_read(sc, IDT_NOR_D0);
	patm_debug(sc, SRAM, "read %04x=%08x", addr, val);
	return (val);
}
static __inline void
patm_sram_write(struct patm_softc *sc, u_int addr, uint32_t val)
{
	patm_debug(sc, SRAM, "write %04x=%08x", addr, val);
	patm_cmd_wait(sc);
	patm_nor_write(sc, IDT_NOR_D0, val);
	patm_cmd_exec(sc, IDT_MKCMD_WSRAM(addr, 0));
}
static __inline void
patm_sram_write4(struct patm_softc *sc, u_int addr, uint32_t v0, uint32_t v1,
    uint32_t v2, uint32_t v3)
{
	patm_debug(sc, SRAM, "write %04x=%08x,%08x,%08x,%08x",
	    addr, v0, v1, v2, v3);
	patm_cmd_wait(sc);
	patm_nor_write(sc, IDT_NOR_D0, v0);
	patm_nor_write(sc, IDT_NOR_D1, v1);
	patm_nor_write(sc, IDT_NOR_D2, v2);
	patm_nor_write(sc, IDT_NOR_D3, v3);
	patm_cmd_exec(sc, IDT_MKCMD_WSRAM(addr, 3));
}

#define	LEGAL_VPI(SC, VPI) \
	(((VPI) & ~((1 << IFP2IFATM((SC)->ifp)->mib.vpi_bits) - 1)) == 0)
#define	LEGAL_VCI(SC, VCI) \
	(((VCI) & ~((1 << IFP2IFATM((SC)->ifp)->mib.vci_bits) - 1)) == 0)

extern const uint32_t patm_rtables155[];
extern const uint32_t patm_rtables25[];
extern const u_int patm_rtables_size;
extern const u_int patm_rtables_ntab;
