521 lines
15 KiB
C
521 lines
15 KiB
C
/*-
|
|
* 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$
|
|
*
|
|
* 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) << (SC)->ifatm.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 ifatm ifatm; /* 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)->ifatm.ifnet, __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)->ifatm.ifnet, "%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 << (SC)->ifatm.mib.vpi_bits) - 1)) == 0)
|
|
#define LEGAL_VCI(SC, VCI) \
|
|
(((VCI) & ~((1 << (SC)->ifatm.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;
|