xref: /onnv-gate/usr/src/uts/intel/io/dnet/dnet.c (revision 11878:ac93462db6d7)
19860Sgdamore@opensolaris.org /*
29860Sgdamore@opensolaris.org  * CDDL HEADER START
39860Sgdamore@opensolaris.org  *
49860Sgdamore@opensolaris.org  * The contents of this file are subject to the terms of the
59860Sgdamore@opensolaris.org  * Common Development and Distribution License (the "License").
69860Sgdamore@opensolaris.org  * You may not use this file except in compliance with the License.
79860Sgdamore@opensolaris.org  *
89860Sgdamore@opensolaris.org  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99860Sgdamore@opensolaris.org  * or http://www.opensolaris.org/os/licensing.
109860Sgdamore@opensolaris.org  * See the License for the specific language governing permissions
119860Sgdamore@opensolaris.org  * and limitations under the License.
129860Sgdamore@opensolaris.org  *
139860Sgdamore@opensolaris.org  * When distributing Covered Code, include this CDDL HEADER in each
149860Sgdamore@opensolaris.org  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159860Sgdamore@opensolaris.org  * If applicable, add the following below this CDDL HEADER, with the
169860Sgdamore@opensolaris.org  * fields enclosed by brackets "[]" replaced with your own identifying
179860Sgdamore@opensolaris.org  * information: Portions Copyright [yyyy] [name of copyright owner]
189860Sgdamore@opensolaris.org  *
199860Sgdamore@opensolaris.org  * CDDL HEADER END
209860Sgdamore@opensolaris.org  */
219860Sgdamore@opensolaris.org 
229860Sgdamore@opensolaris.org /*
23*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
249860Sgdamore@opensolaris.org  * Use is subject to license terms.
259860Sgdamore@opensolaris.org  */
269860Sgdamore@opensolaris.org 
279860Sgdamore@opensolaris.org 
289860Sgdamore@opensolaris.org /*
299860Sgdamore@opensolaris.org  * dnet -- DEC 21x4x
309860Sgdamore@opensolaris.org  *
319860Sgdamore@opensolaris.org  * Currently supports:
329860Sgdamore@opensolaris.org  *	21040, 21041, 21140, 21142, 21143
339860Sgdamore@opensolaris.org  *	SROM versions 1, 3, 3.03, 4
349860Sgdamore@opensolaris.org  *	TP, AUI, BNC, 100BASETX, 100BASET4
359860Sgdamore@opensolaris.org  *
369860Sgdamore@opensolaris.org  * XXX NEEDSWORK
379860Sgdamore@opensolaris.org  *	All media SHOULD work, FX is untested
389860Sgdamore@opensolaris.org  *
3910340Sstallion@opensolaris.org  * Depends on the Generic LAN Driver utility functions in /kernel/misc/mac
409860Sgdamore@opensolaris.org  */
419860Sgdamore@opensolaris.org 
429860Sgdamore@opensolaris.org #define	BUG_4010796	/* See 4007871, 4010796 */
439860Sgdamore@opensolaris.org 
449860Sgdamore@opensolaris.org #include <sys/types.h>
459860Sgdamore@opensolaris.org #include <sys/errno.h>
469860Sgdamore@opensolaris.org #include <sys/param.h>
479860Sgdamore@opensolaris.org #include <sys/stropts.h>
489860Sgdamore@opensolaris.org #include <sys/stream.h>
499860Sgdamore@opensolaris.org #include <sys/kmem.h>
509860Sgdamore@opensolaris.org #include <sys/conf.h>
519860Sgdamore@opensolaris.org #include <sys/devops.h>
529860Sgdamore@opensolaris.org #include <sys/ksynch.h>
539860Sgdamore@opensolaris.org #include <sys/stat.h>
549860Sgdamore@opensolaris.org #include <sys/modctl.h>
559860Sgdamore@opensolaris.org #include <sys/debug.h>
569860Sgdamore@opensolaris.org #include <sys/dlpi.h>
579860Sgdamore@opensolaris.org #include <sys/ethernet.h>
5810340Sstallion@opensolaris.org #include <sys/vlan.h>
5910340Sstallion@opensolaris.org #include <sys/mac.h>
6010340Sstallion@opensolaris.org #include <sys/mac_ether.h>
6110340Sstallion@opensolaris.org #include <sys/mac_provider.h>
629860Sgdamore@opensolaris.org #include <sys/pci.h>
639860Sgdamore@opensolaris.org #include <sys/ddi.h>
649860Sgdamore@opensolaris.org #include <sys/sunddi.h>
6510340Sstallion@opensolaris.org #include <sys/strsun.h>
669860Sgdamore@opensolaris.org 
679860Sgdamore@opensolaris.org #include "dnet_mii.h"
689860Sgdamore@opensolaris.org #include "dnet.h"
699860Sgdamore@opensolaris.org 
709860Sgdamore@opensolaris.org /*
719860Sgdamore@opensolaris.org  *	Declarations and Module Linkage
729860Sgdamore@opensolaris.org  */
739860Sgdamore@opensolaris.org 
749860Sgdamore@opensolaris.org #define	IDENT	"DNET 21x4x"
759860Sgdamore@opensolaris.org 
769860Sgdamore@opensolaris.org /*
779860Sgdamore@opensolaris.org  * #define	DNET_NOISY
789860Sgdamore@opensolaris.org  * #define	SROMDEBUG
799860Sgdamore@opensolaris.org  * #define	SROMDUMPSTRUCTURES
809860Sgdamore@opensolaris.org  */
819860Sgdamore@opensolaris.org 
829860Sgdamore@opensolaris.org #ifdef DNETDEBUG
839860Sgdamore@opensolaris.org #ifdef DNET_NOISY
849860Sgdamore@opensolaris.org int	dnetdebug = -1;
859860Sgdamore@opensolaris.org #else
869860Sgdamore@opensolaris.org int	dnetdebug = 0;
879860Sgdamore@opensolaris.org #endif
889860Sgdamore@opensolaris.org #endif
899860Sgdamore@opensolaris.org 
909860Sgdamore@opensolaris.org /* used for message allocated using desballoc() */
919860Sgdamore@opensolaris.org struct free_ptr {
929860Sgdamore@opensolaris.org 	struct free_rtn	free_rtn;
939860Sgdamore@opensolaris.org 	caddr_t buf;
949860Sgdamore@opensolaris.org };
959860Sgdamore@opensolaris.org 
969860Sgdamore@opensolaris.org struct rbuf_list {
979860Sgdamore@opensolaris.org 	struct rbuf_list	*rbuf_next;	/* next in the list */
989860Sgdamore@opensolaris.org 	caddr_t			rbuf_vaddr;	/* virual addr of the buf */
999860Sgdamore@opensolaris.org 	uint32_t		rbuf_paddr;	/* physical addr of the buf */
1009860Sgdamore@opensolaris.org 	uint32_t		rbuf_endpaddr;	/* physical addr at the end */
1019860Sgdamore@opensolaris.org 	ddi_dma_handle_t	rbuf_dmahdl;	/* dma handle */
1029860Sgdamore@opensolaris.org 	ddi_acc_handle_t	rbuf_acchdl;	/* handle for DDI functions */
1039860Sgdamore@opensolaris.org };
1049860Sgdamore@opensolaris.org 
1059860Sgdamore@opensolaris.org /* Required system entry points */
10610340Sstallion@opensolaris.org static int dnet_probe(dev_info_t *);
10710340Sstallion@opensolaris.org static int dnet_attach(dev_info_t *, ddi_attach_cmd_t);
10810340Sstallion@opensolaris.org static int dnet_detach(dev_info_t *, ddi_detach_cmd_t);
10910340Sstallion@opensolaris.org static int dnet_quiesce(dev_info_t *);
11010340Sstallion@opensolaris.org 
11110340Sstallion@opensolaris.org /* Required driver entry points for GLDv3 */
11210340Sstallion@opensolaris.org static int dnet_m_start(void *);
11310340Sstallion@opensolaris.org static void dnet_m_stop(void *);
11410340Sstallion@opensolaris.org static int dnet_m_getstat(void *, uint_t, uint64_t *);
11510340Sstallion@opensolaris.org static int dnet_m_setpromisc(void *, boolean_t);
11610340Sstallion@opensolaris.org static int dnet_m_multicst(void *, boolean_t, const uint8_t *);
11710340Sstallion@opensolaris.org static int dnet_m_unicst(void *, const uint8_t *);
11810340Sstallion@opensolaris.org static mblk_t *dnet_m_tx(void *, mblk_t *);
11910340Sstallion@opensolaris.org 
12010340Sstallion@opensolaris.org static uint_t dnet_intr(caddr_t);
1219860Sgdamore@opensolaris.org 
1229860Sgdamore@opensolaris.org /* Internal functions used by the above entry points */
1239860Sgdamore@opensolaris.org static void write_gpr(struct dnetinstance *dnetp, uint32_t val);
12410340Sstallion@opensolaris.org static void dnet_reset_board(struct dnetinstance *);
12510340Sstallion@opensolaris.org static void dnet_init_board(struct dnetinstance *);
12610340Sstallion@opensolaris.org static void dnet_chip_init(struct dnetinstance *);
12710340Sstallion@opensolaris.org static uint32_t hashindex(const uint8_t *);
12810340Sstallion@opensolaris.org static int dnet_start(struct dnetinstance *);
12910340Sstallion@opensolaris.org static int dnet_set_addr(struct dnetinstance *);
13010340Sstallion@opensolaris.org 
13110340Sstallion@opensolaris.org static boolean_t dnet_send(struct dnetinstance *, mblk_t *);
13210340Sstallion@opensolaris.org 
13310340Sstallion@opensolaris.org static void dnet_getp(struct dnetinstance *);
13410340Sstallion@opensolaris.org static void update_rx_stats(struct dnetinstance *, int);
13510340Sstallion@opensolaris.org static void update_tx_stats(struct dnetinstance *, int);
1369860Sgdamore@opensolaris.org 
1379860Sgdamore@opensolaris.org /* Media Selection Setup Routines */
13810340Sstallion@opensolaris.org static void set_gpr(struct dnetinstance *);
13910340Sstallion@opensolaris.org static void set_opr(struct dnetinstance *);
14010340Sstallion@opensolaris.org static void set_sia(struct dnetinstance *);
1419860Sgdamore@opensolaris.org 
1429860Sgdamore@opensolaris.org /* Buffer Management Routines */
14310340Sstallion@opensolaris.org static int dnet_alloc_bufs(struct dnetinstance *);
14410340Sstallion@opensolaris.org static void dnet_free_bufs(struct dnetinstance *);
14510340Sstallion@opensolaris.org static void dnet_init_txrx_bufs(struct dnetinstance *);
14610340Sstallion@opensolaris.org static int alloc_descriptor(struct dnetinstance *);
14710340Sstallion@opensolaris.org static void dnet_reclaim_Tx_desc(struct dnetinstance *);
1489860Sgdamore@opensolaris.org static int dnet_rbuf_init(dev_info_t *, int);
1499860Sgdamore@opensolaris.org static int dnet_rbuf_destroy();
1509860Sgdamore@opensolaris.org static struct rbuf_list *dnet_rbuf_alloc(dev_info_t *, int);
1519860Sgdamore@opensolaris.org static void dnet_rbuf_free(caddr_t);
1529860Sgdamore@opensolaris.org static void dnet_freemsg_buf(struct free_ptr *);
1539860Sgdamore@opensolaris.org 
15410340Sstallion@opensolaris.org static void setup_block(struct dnetinstance *);
1559860Sgdamore@opensolaris.org 
1569860Sgdamore@opensolaris.org /* SROM read functions */
1579860Sgdamore@opensolaris.org static int dnet_read_srom(dev_info_t *, int, ddi_acc_handle_t, caddr_t,
1589860Sgdamore@opensolaris.org     uchar_t *, int);
1599860Sgdamore@opensolaris.org static void dnet_read21040addr(dev_info_t *, ddi_acc_handle_t, caddr_t,
1609860Sgdamore@opensolaris.org     uchar_t *, int *);
1619860Sgdamore@opensolaris.org static void dnet_read21140srom(ddi_acc_handle_t, caddr_t, uchar_t *, int);
1629860Sgdamore@opensolaris.org static int get_alternative_srom_image(dev_info_t *, uchar_t *, int);
1639860Sgdamore@opensolaris.org static void dnet_print_srom(SROM_FORMAT *sr);
1649860Sgdamore@opensolaris.org static void dnet_dump_leaf(LEAF_FORMAT *leaf);
1659860Sgdamore@opensolaris.org static void dnet_dump_block(media_block_t *block);
1669860Sgdamore@opensolaris.org #ifdef BUG_4010796
1679860Sgdamore@opensolaris.org static void set_alternative_srom_image(dev_info_t *, uchar_t *, int);
16810340Sstallion@opensolaris.org static int dnet_hack(dev_info_t *);
1699860Sgdamore@opensolaris.org #endif
1709860Sgdamore@opensolaris.org 
17110340Sstallion@opensolaris.org static int dnet_hack_interrupts(struct dnetinstance *, int);
1729860Sgdamore@opensolaris.org static int dnet_detach_hacked_interrupt(dev_info_t *devinfo);
17310340Sstallion@opensolaris.org static void enable_interrupts(struct dnetinstance *);
1749860Sgdamore@opensolaris.org 
1759860Sgdamore@opensolaris.org /* SROM parsing functions */
1769860Sgdamore@opensolaris.org static void dnet_parse_srom(struct dnetinstance *dnetp, SROM_FORMAT *sr,
1779860Sgdamore@opensolaris.org     uchar_t *vi);
1789860Sgdamore@opensolaris.org static void parse_controller_leaf(struct dnetinstance *dnetp, LEAF_FORMAT *leaf,
1799860Sgdamore@opensolaris.org     uchar_t *vi);
1809860Sgdamore@opensolaris.org static uchar_t *parse_media_block(struct dnetinstance *dnetp,
1819860Sgdamore@opensolaris.org     media_block_t *block, uchar_t *vi);
1829860Sgdamore@opensolaris.org static int check_srom_valid(uchar_t *);
1839860Sgdamore@opensolaris.org static void dnet_dumpbin(char *msg, uchar_t *, int size, int len);
1849860Sgdamore@opensolaris.org static void setup_legacy_blocks();
1859860Sgdamore@opensolaris.org /* Active Media Determination Routines */
18610340Sstallion@opensolaris.org static void find_active_media(struct dnetinstance *);
18710340Sstallion@opensolaris.org static int send_test_packet(struct dnetinstance *);
18810340Sstallion@opensolaris.org static int dnet_link_sense(struct dnetinstance *);
1899860Sgdamore@opensolaris.org 
1909860Sgdamore@opensolaris.org /* PHY MII Routines */
1919860Sgdamore@opensolaris.org static ushort_t dnet_mii_read(dev_info_t *dip, int phy_addr, int reg_num);
1929860Sgdamore@opensolaris.org static void dnet_mii_write(dev_info_t *dip, int phy_addr, int reg_num,
1939860Sgdamore@opensolaris.org 			int reg_dat);
1949860Sgdamore@opensolaris.org static void write_mii(struct dnetinstance *, uint32_t, int);
1959860Sgdamore@opensolaris.org static void mii_tristate(struct dnetinstance *);
19610340Sstallion@opensolaris.org static void do_phy(struct dnetinstance *);
1979860Sgdamore@opensolaris.org static void dnet_mii_link_cb(dev_info_t *, int, enum mii_phy_state);
1989860Sgdamore@opensolaris.org static void set_leaf(SROM_FORMAT *sr, LEAF_FORMAT *leaf);
1999860Sgdamore@opensolaris.org 
2009860Sgdamore@opensolaris.org #ifdef DNETDEBUG
2019860Sgdamore@opensolaris.org uint32_t dnet_usecelapsed(struct dnetinstance *dnetp);
2029860Sgdamore@opensolaris.org void dnet_timestamp(struct dnetinstance *, char *);
2039860Sgdamore@opensolaris.org void dnet_usectimeout(struct dnetinstance *, uint32_t, int, timercb_t);
2049860Sgdamore@opensolaris.org #endif
2059860Sgdamore@opensolaris.org static char *media_str[] = {
2069860Sgdamore@opensolaris.org 	"10BaseT",
2079860Sgdamore@opensolaris.org 	"10Base2",
2089860Sgdamore@opensolaris.org 	"10Base5",
2099860Sgdamore@opensolaris.org 	"100BaseTX",
2109860Sgdamore@opensolaris.org 	"10BaseT FD",
2119860Sgdamore@opensolaris.org 	"100BaseTX FD",
2129860Sgdamore@opensolaris.org 	"100BaseT4",
2139860Sgdamore@opensolaris.org 	"100BaseFX",
2149860Sgdamore@opensolaris.org 	"100BaseFX FD",
2159860Sgdamore@opensolaris.org 	"MII"
2169860Sgdamore@opensolaris.org };
2179860Sgdamore@opensolaris.org 
2189860Sgdamore@opensolaris.org /* default SROM info for cards with no SROMs */
2199860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_default_100;
2209860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_asante;
2219860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_phylegacy;
2229860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_cogent_100;
2239860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_21041;
2249860Sgdamore@opensolaris.org static LEAF_FORMAT leaf_21040;
2259860Sgdamore@opensolaris.org 
22610340Sstallion@opensolaris.org /* rx buffer size (rounded up to 4) */
22710340Sstallion@opensolaris.org int rx_buf_size = (ETHERMAX + ETHERFCSL + VLAN_TAGSZ + 3) & ~3;
2289860Sgdamore@opensolaris.org 
2299860Sgdamore@opensolaris.org int max_rx_desc_21040 = MAX_RX_DESC_21040;
2309860Sgdamore@opensolaris.org int max_rx_desc_21140 = MAX_RX_DESC_21140;
2319860Sgdamore@opensolaris.org int max_tx_desc = MAX_TX_DESC;
2329860Sgdamore@opensolaris.org int dnet_xmit_threshold = MAX_TX_DESC >> 2;	/* XXX need tuning? */
2339860Sgdamore@opensolaris.org 
2349860Sgdamore@opensolaris.org static kmutex_t dnet_rbuf_lock;		/* mutex to protect rbuf_list data */
2359860Sgdamore@opensolaris.org 
2369860Sgdamore@opensolaris.org /* used for buffers allocated by ddi_dma_mem_alloc() */
2379860Sgdamore@opensolaris.org static ddi_dma_attr_t dma_attr = {
2389860Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* dma_attr version */
2399860Sgdamore@opensolaris.org 	0,			/* dma_attr_addr_lo */
2409860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_addr_hi */
2419860Sgdamore@opensolaris.org 	0x7FFFFFFF,		/* dma_attr_count_max */
2429860Sgdamore@opensolaris.org 	4,			/* dma_attr_align */
2439860Sgdamore@opensolaris.org 	0x3F,			/* dma_attr_burstsizes */
2449860Sgdamore@opensolaris.org 	1,			/* dma_attr_minxfer */
2459860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_maxxfer */
2469860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_seg */
2479860Sgdamore@opensolaris.org 	1,			/* dma_attr_sgllen */
2489860Sgdamore@opensolaris.org 	1,			/* dma_attr_granular */
2499860Sgdamore@opensolaris.org 	0,			/* dma_attr_flags */
2509860Sgdamore@opensolaris.org };
2519860Sgdamore@opensolaris.org 
2529860Sgdamore@opensolaris.org /* used for buffers allocated for rbuf, allow 2 cookies */
2539860Sgdamore@opensolaris.org static ddi_dma_attr_t dma_attr_rb = {
2549860Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* dma_attr version */
2559860Sgdamore@opensolaris.org 	0,			/* dma_attr_addr_lo */
2569860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_addr_hi */
2579860Sgdamore@opensolaris.org 	0x7FFFFFFF,		/* dma_attr_count_max */
2589860Sgdamore@opensolaris.org 	4,			/* dma_attr_align */
2599860Sgdamore@opensolaris.org 	0x3F,			/* dma_attr_burstsizes */
2609860Sgdamore@opensolaris.org 	1,			/* dma_attr_minxfer */
2619860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_maxxfer */
2629860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_seg */
2639860Sgdamore@opensolaris.org 	2,			/* dma_attr_sgllen */
2649860Sgdamore@opensolaris.org 	1,			/* dma_attr_granular */
2659860Sgdamore@opensolaris.org 	0,			/* dma_attr_flags */
2669860Sgdamore@opensolaris.org };
2679860Sgdamore@opensolaris.org /* used for buffers which are NOT from ddi_dma_mem_alloc() - xmit side */
2689860Sgdamore@opensolaris.org static ddi_dma_attr_t dma_attr_tx = {
2699860Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* dma_attr version */
2709860Sgdamore@opensolaris.org 	0,			/* dma_attr_addr_lo */
2719860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_addr_hi */
2729860Sgdamore@opensolaris.org 	0x7FFFFFFF,		/* dma_attr_count_max */
2739860Sgdamore@opensolaris.org 	1,			/* dma_attr_align */
2749860Sgdamore@opensolaris.org 	0x3F,			/* dma_attr_burstsizes */
2759860Sgdamore@opensolaris.org 	1,			/* dma_attr_minxfer */
2769860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_maxxfer */
2779860Sgdamore@opensolaris.org 	(uint64_t)0xFFFFFFFF,	/* dma_attr_seg */
2789860Sgdamore@opensolaris.org 	0x7FFF,			/* dma_attr_sgllen */
2799860Sgdamore@opensolaris.org 	1,			/* dma_attr_granular */
2809860Sgdamore@opensolaris.org 	0,			/* dma_attr_flags */
2819860Sgdamore@opensolaris.org };
2829860Sgdamore@opensolaris.org 
2839860Sgdamore@opensolaris.org static ddi_device_acc_attr_t accattr = {
2849860Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
2859860Sgdamore@opensolaris.org 	DDI_NEVERSWAP_ACC,
2869860Sgdamore@opensolaris.org 	DDI_STRICTORDER_ACC,
2879860Sgdamore@opensolaris.org };
2889860Sgdamore@opensolaris.org 
2899860Sgdamore@opensolaris.org uchar_t dnet_broadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
2909860Sgdamore@opensolaris.org 
2919860Sgdamore@opensolaris.org /* Standard Module linkage initialization for a Streams driver */
2929860Sgdamore@opensolaris.org extern struct mod_ops mod_driverops;
2939860Sgdamore@opensolaris.org 
29410340Sstallion@opensolaris.org DDI_DEFINE_STREAM_OPS(dnet_devops, nulldev, dnet_probe, dnet_attach,
29510340Sstallion@opensolaris.org     dnet_detach, nodev, NULL, D_MP, NULL, dnet_quiesce);
29610340Sstallion@opensolaris.org 
29710340Sstallion@opensolaris.org static struct modldrv dnet_modldrv = {
2989860Sgdamore@opensolaris.org 	&mod_driverops,		/* Type of module.  This one is a driver */
2999860Sgdamore@opensolaris.org 	IDENT,			/* short description */
30010340Sstallion@opensolaris.org 	&dnet_devops		/* driver specific ops */
30110340Sstallion@opensolaris.org };
30210340Sstallion@opensolaris.org 
30310340Sstallion@opensolaris.org static struct modlinkage dnet_modlinkage = {
30410340Sstallion@opensolaris.org 	MODREV_1,		/* ml_rev */
30510340Sstallion@opensolaris.org 	{ &dnet_modldrv, NULL }	/* ml_linkage */
3069860Sgdamore@opensolaris.org };
3079860Sgdamore@opensolaris.org 
30810340Sstallion@opensolaris.org static mac_callbacks_t dnet_m_callbacks = {
30910340Sstallion@opensolaris.org 	0,			/* mc_callbacks */
31010340Sstallion@opensolaris.org 	dnet_m_getstat,		/* mc_getstat */
31110340Sstallion@opensolaris.org 	dnet_m_start,		/* mc_start */
31210340Sstallion@opensolaris.org 	dnet_m_stop,		/* mc_stop */
31310340Sstallion@opensolaris.org 	dnet_m_setpromisc,	/* mc_setpromisc */
31410340Sstallion@opensolaris.org 	dnet_m_multicst,	/* mc_multicst */
31510340Sstallion@opensolaris.org 	dnet_m_unicst,		/* mc_unicst */
31610340Sstallion@opensolaris.org 	dnet_m_tx,		/* mc_tx */
317*11878SVenu.Iyer@Sun.COM 	NULL,
31810340Sstallion@opensolaris.org 	NULL,			/* mc_ioctl */
31910340Sstallion@opensolaris.org 	NULL,			/* mc_getcapab */
32010340Sstallion@opensolaris.org 	NULL,			/* mc_open */
32110340Sstallion@opensolaris.org 	NULL			/* mc_close */
3229860Sgdamore@opensolaris.org };
3239860Sgdamore@opensolaris.org 
3249860Sgdamore@opensolaris.org /*
3259860Sgdamore@opensolaris.org  * Passed to the hacked interrupt for multiport Cogent and ZNYX cards with
3269860Sgdamore@opensolaris.org  * dodgy interrupt routing
3279860Sgdamore@opensolaris.org  */
3289860Sgdamore@opensolaris.org #define	MAX_INST 8 /* Maximum instances on a multiport adapter. */
3299860Sgdamore@opensolaris.org struct hackintr_inf
3309860Sgdamore@opensolaris.org {
33110340Sstallion@opensolaris.org 	struct dnetinstance *dnetps[MAX_INST]; /* dnetps for each port */
3329860Sgdamore@opensolaris.org 	dev_info_t *devinfo;		    /* Devinfo of the primary device */
3339860Sgdamore@opensolaris.org 	kmutex_t lock;
3349860Sgdamore@opensolaris.org 		/* Ensures the interrupt doesn't get called while detaching */
3359860Sgdamore@opensolaris.org };
3369860Sgdamore@opensolaris.org static char hackintr_propname[] = "InterruptData";
3379860Sgdamore@opensolaris.org static char macoffset_propname[] = "MAC_offset";
3389860Sgdamore@opensolaris.org static char speed_propname[] = "speed";
3399860Sgdamore@opensolaris.org static char ofloprob_propname[] = "dmaworkaround";
3409860Sgdamore@opensolaris.org static char duplex_propname[] = "full-duplex"; /* Must agree with MII */
3419860Sgdamore@opensolaris.org static char printsrom_propname[] = "print-srom";
3429860Sgdamore@opensolaris.org 
3439860Sgdamore@opensolaris.org static uint_t dnet_hack_intr(struct hackintr_inf *);
3449860Sgdamore@opensolaris.org 
3459860Sgdamore@opensolaris.org int
_init(void)3469860Sgdamore@opensolaris.org _init(void)
3479860Sgdamore@opensolaris.org {
3489860Sgdamore@opensolaris.org 	int i;
3499860Sgdamore@opensolaris.org 
3509860Sgdamore@opensolaris.org 	/* Configure fake sroms for legacy cards */
3519860Sgdamore@opensolaris.org 	mutex_init(&dnet_rbuf_lock, NULL, MUTEX_DRIVER, NULL);
3529860Sgdamore@opensolaris.org 	setup_legacy_blocks();
35310340Sstallion@opensolaris.org 
35410340Sstallion@opensolaris.org 	mac_init_ops(&dnet_devops, "dnet");
35510340Sstallion@opensolaris.org 
35610340Sstallion@opensolaris.org 	if ((i = mod_install(&dnet_modlinkage)) != 0) {
35710340Sstallion@opensolaris.org 		mac_fini_ops(&dnet_devops);
3589860Sgdamore@opensolaris.org 		mutex_destroy(&dnet_rbuf_lock);
3599860Sgdamore@opensolaris.org 	}
3609860Sgdamore@opensolaris.org 	return (i);
3619860Sgdamore@opensolaris.org }
3629860Sgdamore@opensolaris.org 
3639860Sgdamore@opensolaris.org int
_fini(void)3649860Sgdamore@opensolaris.org _fini(void)
3659860Sgdamore@opensolaris.org {
3669860Sgdamore@opensolaris.org 	int i;
36710340Sstallion@opensolaris.org 
36810340Sstallion@opensolaris.org 	if ((i = mod_remove(&dnet_modlinkage)) == 0) {
36910340Sstallion@opensolaris.org 		mac_fini_ops(&dnet_devops);
37010340Sstallion@opensolaris.org 
3719860Sgdamore@opensolaris.org 		/* loop until all the receive buffers are freed */
3729860Sgdamore@opensolaris.org 		while (dnet_rbuf_destroy() != 0) {
3739860Sgdamore@opensolaris.org 			delay(drv_usectohz(100000));
3749860Sgdamore@opensolaris.org #ifdef DNETDEBUG
3759860Sgdamore@opensolaris.org 			if (dnetdebug & DNETDDI)
3769860Sgdamore@opensolaris.org 				cmn_err(CE_WARN, "dnet _fini delay");
3779860Sgdamore@opensolaris.org #endif
3789860Sgdamore@opensolaris.org 		}
3799860Sgdamore@opensolaris.org 		mutex_destroy(&dnet_rbuf_lock);
3809860Sgdamore@opensolaris.org 	}
3819860Sgdamore@opensolaris.org 	return (i);
3829860Sgdamore@opensolaris.org }
3839860Sgdamore@opensolaris.org 
3849860Sgdamore@opensolaris.org int
_info(struct modinfo * modinfop)3859860Sgdamore@opensolaris.org _info(struct modinfo *modinfop)
3869860Sgdamore@opensolaris.org {
38710340Sstallion@opensolaris.org 	return (mod_info(&dnet_modlinkage, modinfop));
3889860Sgdamore@opensolaris.org }
3899860Sgdamore@opensolaris.org 
3909860Sgdamore@opensolaris.org /*
3919860Sgdamore@opensolaris.org  * probe(9E) -- Determine if a device is present
3929860Sgdamore@opensolaris.org  */
3939860Sgdamore@opensolaris.org static int
dnet_probe(dev_info_t * devinfo)39410340Sstallion@opensolaris.org dnet_probe(dev_info_t *devinfo)
3959860Sgdamore@opensolaris.org {
3969860Sgdamore@opensolaris.org 	ddi_acc_handle_t handle;
3979860Sgdamore@opensolaris.org 	uint16_t	vendorid;
3989860Sgdamore@opensolaris.org 	uint16_t	deviceid;
3999860Sgdamore@opensolaris.org 
4009860Sgdamore@opensolaris.org 	if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
4019860Sgdamore@opensolaris.org 		return (DDI_PROBE_FAILURE);
4029860Sgdamore@opensolaris.org 
4039860Sgdamore@opensolaris.org 	vendorid = pci_config_get16(handle, PCI_CONF_VENID);
4049860Sgdamore@opensolaris.org 
4059860Sgdamore@opensolaris.org 	if (vendorid != DEC_VENDOR_ID) {
4069860Sgdamore@opensolaris.org 		pci_config_teardown(&handle);
4079860Sgdamore@opensolaris.org 		return (DDI_PROBE_FAILURE);
4089860Sgdamore@opensolaris.org 	}
4099860Sgdamore@opensolaris.org 
4109860Sgdamore@opensolaris.org 	deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
4119860Sgdamore@opensolaris.org 	switch (deviceid) {
4129860Sgdamore@opensolaris.org 	case DEVICE_ID_21040:
4139860Sgdamore@opensolaris.org 	case DEVICE_ID_21041:
4149860Sgdamore@opensolaris.org 	case DEVICE_ID_21140:
4159860Sgdamore@opensolaris.org 	case DEVICE_ID_21143: /* And 142 */
4169860Sgdamore@opensolaris.org 		break;
4179860Sgdamore@opensolaris.org 	default:
4189860Sgdamore@opensolaris.org 		pci_config_teardown(&handle);
4199860Sgdamore@opensolaris.org 		return (DDI_PROBE_FAILURE);
4209860Sgdamore@opensolaris.org 	}
4219860Sgdamore@opensolaris.org 
4229860Sgdamore@opensolaris.org 	pci_config_teardown(&handle);
4239860Sgdamore@opensolaris.org #ifndef BUG_4010796
4249860Sgdamore@opensolaris.org 	return (DDI_PROBE_SUCCESS);
4259860Sgdamore@opensolaris.org #else
42610340Sstallion@opensolaris.org 	return (dnet_hack(devinfo));
4279860Sgdamore@opensolaris.org #endif
4289860Sgdamore@opensolaris.org }
4299860Sgdamore@opensolaris.org 
4309860Sgdamore@opensolaris.org #ifdef BUG_4010796
4319860Sgdamore@opensolaris.org /*
4329860Sgdamore@opensolaris.org  * If we have a device, but we cannot presently access its SROM data,
4339860Sgdamore@opensolaris.org  * then we return DDI_PROBE_PARTIAL and hope that sometime later we
4349860Sgdamore@opensolaris.org  * will be able to get at the SROM data.  This can only happen if we
4359860Sgdamore@opensolaris.org  * are a secondary port with no SROM, and the bootstrap failed to set
4369860Sgdamore@opensolaris.org  * our DNET_SROM property, and our primary sibling has not yet probed.
4379860Sgdamore@opensolaris.org  */
4389860Sgdamore@opensolaris.org static int
dnet_hack(dev_info_t * devinfo)43910340Sstallion@opensolaris.org dnet_hack(dev_info_t *devinfo)
4409860Sgdamore@opensolaris.org {
4419860Sgdamore@opensolaris.org 	uchar_t 	vendor_info[SROM_SIZE];
4429860Sgdamore@opensolaris.org 	uint32_t	csr;
4439860Sgdamore@opensolaris.org 	uint16_t	deviceid;
4449860Sgdamore@opensolaris.org 	ddi_acc_handle_t handle;
4459860Sgdamore@opensolaris.org 	uint32_t	retval;
4469860Sgdamore@opensolaris.org 	int		secondary;
4479860Sgdamore@opensolaris.org 	ddi_acc_handle_t io_handle;
4489860Sgdamore@opensolaris.org 	caddr_t		io_reg;
4499860Sgdamore@opensolaris.org 
4509860Sgdamore@opensolaris.org #define	DNET_PCI_RNUMBER	1
4519860Sgdamore@opensolaris.org 
4529860Sgdamore@opensolaris.org 	if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
4539860Sgdamore@opensolaris.org 		return (DDI_PROBE_FAILURE);
4549860Sgdamore@opensolaris.org 
4559860Sgdamore@opensolaris.org 	deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
4569860Sgdamore@opensolaris.org 
4579860Sgdamore@opensolaris.org 	/*
4589860Sgdamore@opensolaris.org 	 * Turn on Master Enable and IO Enable bits.
4599860Sgdamore@opensolaris.org 	 */
4609860Sgdamore@opensolaris.org 	csr = pci_config_get32(handle, PCI_CONF_COMM);
4619860Sgdamore@opensolaris.org 	pci_config_put32(handle, PCI_CONF_COMM, (csr |PCI_COMM_ME|PCI_COMM_IO));
4629860Sgdamore@opensolaris.org 
4639860Sgdamore@opensolaris.org 	pci_config_teardown(&handle);
4649860Sgdamore@opensolaris.org 
4659860Sgdamore@opensolaris.org 	/* Now map I/O register */
4669860Sgdamore@opensolaris.org 	if (ddi_regs_map_setup(devinfo, DNET_PCI_RNUMBER,
4679860Sgdamore@opensolaris.org 	    &io_reg, 0, 0, &accattr, &io_handle) != DDI_SUCCESS) {
4689860Sgdamore@opensolaris.org 		return (DDI_PROBE_FAILURE);
4699860Sgdamore@opensolaris.org 	}
4709860Sgdamore@opensolaris.org 
4719860Sgdamore@opensolaris.org 	/*
4729860Sgdamore@opensolaris.org 	 * Reset the chip
4739860Sgdamore@opensolaris.org 	 */
4749860Sgdamore@opensolaris.org 	ddi_put32(io_handle, REG32(io_reg, BUS_MODE_REG), SW_RESET);
4759860Sgdamore@opensolaris.org 	drv_usecwait(3);
4769860Sgdamore@opensolaris.org 	ddi_put32(io_handle, REG32(io_reg, BUS_MODE_REG), 0);
4779860Sgdamore@opensolaris.org 	drv_usecwait(8);
4789860Sgdamore@opensolaris.org 
4799860Sgdamore@opensolaris.org 	secondary = dnet_read_srom(devinfo, deviceid, io_handle,
4809860Sgdamore@opensolaris.org 	    io_reg, vendor_info, sizeof (vendor_info));
4819860Sgdamore@opensolaris.org 
4829860Sgdamore@opensolaris.org 	switch (secondary) {
4839860Sgdamore@opensolaris.org 	case -1:
4849860Sgdamore@opensolaris.org 		/* We can't access our SROM data! */
4859860Sgdamore@opensolaris.org 		retval = DDI_PROBE_PARTIAL;
4869860Sgdamore@opensolaris.org 		break;
4879860Sgdamore@opensolaris.org 	case 0:
4889860Sgdamore@opensolaris.org 		retval = DDI_PROBE_SUCCESS;
4899860Sgdamore@opensolaris.org 		break;
4909860Sgdamore@opensolaris.org 	default:
4919860Sgdamore@opensolaris.org 		retval = DDI_PROBE_SUCCESS;
4929860Sgdamore@opensolaris.org 	}
4939860Sgdamore@opensolaris.org 
4949860Sgdamore@opensolaris.org 	ddi_regs_map_free(&io_handle);
4959860Sgdamore@opensolaris.org 	return (retval);
4969860Sgdamore@opensolaris.org }
4979860Sgdamore@opensolaris.org #endif /* BUG_4010796 */
4989860Sgdamore@opensolaris.org 
4999860Sgdamore@opensolaris.org /*
5009860Sgdamore@opensolaris.org  * attach(9E) -- Attach a device to the system
5019860Sgdamore@opensolaris.org  *
5029860Sgdamore@opensolaris.org  * Called once for each board successfully probed.
5039860Sgdamore@opensolaris.org  */
5049860Sgdamore@opensolaris.org static int
dnet_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)50510340Sstallion@opensolaris.org dnet_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
5069860Sgdamore@opensolaris.org {
5079860Sgdamore@opensolaris.org 	uint16_t revid;
5089860Sgdamore@opensolaris.org 	struct dnetinstance 	*dnetp;		/* Our private device info */
50910340Sstallion@opensolaris.org 	mac_register_t		*macp;
5109860Sgdamore@opensolaris.org 	uchar_t 		vendor_info[SROM_SIZE];
5119860Sgdamore@opensolaris.org 	uint32_t		csr;
5129860Sgdamore@opensolaris.org 	uint16_t		deviceid;
5139860Sgdamore@opensolaris.org 	ddi_acc_handle_t 	handle;
5149860Sgdamore@opensolaris.org 	int			secondary;
5159860Sgdamore@opensolaris.org 
5169860Sgdamore@opensolaris.org #define	DNET_PCI_RNUMBER	1
5179860Sgdamore@opensolaris.org 
5189860Sgdamore@opensolaris.org 	switch (cmd) {
5199860Sgdamore@opensolaris.org 	case DDI_ATTACH:
5209860Sgdamore@opensolaris.org 		break;
5219860Sgdamore@opensolaris.org 
5229860Sgdamore@opensolaris.org 	case DDI_RESUME:
52310340Sstallion@opensolaris.org 		/* Get the driver private (dnetinstance) structure */
52410340Sstallion@opensolaris.org 		dnetp = ddi_get_driver_private(devinfo);
5259860Sgdamore@opensolaris.org 
5269860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->intrlock);
5279860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->txlock);
52810340Sstallion@opensolaris.org 		dnet_reset_board(dnetp);
52910340Sstallion@opensolaris.org 		dnet_init_board(dnetp);
5309860Sgdamore@opensolaris.org 		dnetp->suspended = B_FALSE;
5319860Sgdamore@opensolaris.org 
5329860Sgdamore@opensolaris.org 		if (dnetp->running) {
53310340Sstallion@opensolaris.org 			dnetp->need_tx_update = B_FALSE;
5349860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->txlock);
53510340Sstallion@opensolaris.org 			(void) dnet_start(dnetp);
5369860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->intrlock);
53710340Sstallion@opensolaris.org 			mac_tx_update(dnetp->mac_handle);
5389860Sgdamore@opensolaris.org 		} else {
5399860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->txlock);
5409860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->intrlock);
5419860Sgdamore@opensolaris.org 		}
5429860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
5439860Sgdamore@opensolaris.org 	default:
5449860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
5459860Sgdamore@opensolaris.org 	}
54610340Sstallion@opensolaris.org 
5479860Sgdamore@opensolaris.org 	if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
5489860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
5499860Sgdamore@opensolaris.org 
5509860Sgdamore@opensolaris.org 	deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
5519860Sgdamore@opensolaris.org 	switch (deviceid) {
5529860Sgdamore@opensolaris.org 	case DEVICE_ID_21040:
5539860Sgdamore@opensolaris.org 	case DEVICE_ID_21041:
5549860Sgdamore@opensolaris.org 	case DEVICE_ID_21140:
5559860Sgdamore@opensolaris.org 	case DEVICE_ID_21143: /* And 142 */
5569860Sgdamore@opensolaris.org 		break;
5579860Sgdamore@opensolaris.org 	default:
5589860Sgdamore@opensolaris.org 		pci_config_teardown(&handle);
5599860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
5609860Sgdamore@opensolaris.org 	}
5619860Sgdamore@opensolaris.org 
5629860Sgdamore@opensolaris.org 	/*
5639860Sgdamore@opensolaris.org 	 * Turn on Master Enable and IO Enable bits.
5649860Sgdamore@opensolaris.org 	 */
5659860Sgdamore@opensolaris.org 	csr = pci_config_get32(handle, PCI_CONF_COMM);
5669860Sgdamore@opensolaris.org 	pci_config_put32(handle, PCI_CONF_COMM, (csr |PCI_COMM_ME|PCI_COMM_IO));
5679860Sgdamore@opensolaris.org 
5689860Sgdamore@opensolaris.org 	/* Make sure the device is not asleep */
5699860Sgdamore@opensolaris.org 	csr = pci_config_get32(handle, PCI_DNET_CONF_CFDD);
5709860Sgdamore@opensolaris.org 	pci_config_put32(handle, PCI_DNET_CONF_CFDD,
5719860Sgdamore@opensolaris.org 	    csr &  ~(CFDD_SLEEP|CFDD_SNOOZE));
5729860Sgdamore@opensolaris.org 
5739860Sgdamore@opensolaris.org 	revid = pci_config_get8(handle, PCI_CONF_REVID);
5749860Sgdamore@opensolaris.org 	pci_config_teardown(&handle);
5759860Sgdamore@opensolaris.org 
57610340Sstallion@opensolaris.org 	dnetp = kmem_zalloc(sizeof (struct dnetinstance), KM_SLEEP);
57710340Sstallion@opensolaris.org 	ddi_set_driver_private(devinfo, dnetp);
5789860Sgdamore@opensolaris.org 
5799860Sgdamore@opensolaris.org 	/* Now map I/O register */
5809860Sgdamore@opensolaris.org 	if (ddi_regs_map_setup(devinfo, DNET_PCI_RNUMBER, &dnetp->io_reg,
5819860Sgdamore@opensolaris.org 	    0, 0, &accattr, &dnetp->io_handle) != DDI_SUCCESS) {
5829860Sgdamore@opensolaris.org 		kmem_free(dnetp, sizeof (struct dnetinstance));
5839860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
5849860Sgdamore@opensolaris.org 	}
5859860Sgdamore@opensolaris.org 
5869860Sgdamore@opensolaris.org 	dnetp->devinfo = devinfo;
5879860Sgdamore@opensolaris.org 	dnetp->board_type = deviceid;
5889860Sgdamore@opensolaris.org 
5899860Sgdamore@opensolaris.org 	/*
5909860Sgdamore@opensolaris.org 	 * Get the iblock cookie with which to initialize the mutexes.
5919860Sgdamore@opensolaris.org 	 */
59210340Sstallion@opensolaris.org 	if (ddi_get_iblock_cookie(devinfo, 0, &dnetp->icookie)
5939860Sgdamore@opensolaris.org 	    != DDI_SUCCESS)
5949860Sgdamore@opensolaris.org 		goto fail;
5959860Sgdamore@opensolaris.org 
5969860Sgdamore@opensolaris.org 	/*
5979860Sgdamore@opensolaris.org 	 * Initialize mutex's for this device.
5989860Sgdamore@opensolaris.org 	 * Do this before registering the interrupt handler to avoid
5999860Sgdamore@opensolaris.org 	 * condition where interrupt handler can try using uninitialized
6009860Sgdamore@opensolaris.org 	 * mutex.
6019860Sgdamore@opensolaris.org 	 * Lock ordering rules: always lock intrlock first before
6029860Sgdamore@opensolaris.org 	 * txlock if both are required.
6039860Sgdamore@opensolaris.org 	 */
60410340Sstallion@opensolaris.org 	mutex_init(&dnetp->txlock, NULL, MUTEX_DRIVER, dnetp->icookie);
60510340Sstallion@opensolaris.org 	mutex_init(&dnetp->intrlock, NULL, MUTEX_DRIVER, dnetp->icookie);
6069860Sgdamore@opensolaris.org 
6079860Sgdamore@opensolaris.org 	/*
6089860Sgdamore@opensolaris.org 	 * Get the BNC/TP indicator from the conf file for 21040
6099860Sgdamore@opensolaris.org 	 */
6109860Sgdamore@opensolaris.org 	dnetp->bnc_indicator =
6119860Sgdamore@opensolaris.org 	    ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
6129860Sgdamore@opensolaris.org 	    "bncaui", -1);
6139860Sgdamore@opensolaris.org 
6149860Sgdamore@opensolaris.org 	/*
6159860Sgdamore@opensolaris.org 	 * For 21140 check the data rate set in the conf file. Default is
6169860Sgdamore@opensolaris.org 	 * 100Mb/s. Disallow connections at settings that would conflict
6179860Sgdamore@opensolaris.org 	 * with what's in the conf file
6189860Sgdamore@opensolaris.org 	 */
6199860Sgdamore@opensolaris.org 	dnetp->speed =
6209860Sgdamore@opensolaris.org 	    ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
6219860Sgdamore@opensolaris.org 	    speed_propname, 0);
6229860Sgdamore@opensolaris.org 	dnetp->full_duplex =
6239860Sgdamore@opensolaris.org 	    ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
6249860Sgdamore@opensolaris.org 	    duplex_propname, -1);
6259860Sgdamore@opensolaris.org 
6269860Sgdamore@opensolaris.org 	if (dnetp->speed == 100) {
6279860Sgdamore@opensolaris.org 		dnetp->disallowed_media |= (1UL<<MEDIA_TP) | (1UL<<MEDIA_TP_FD);
6289860Sgdamore@opensolaris.org 	} else if (dnetp->speed == 10) {
6299860Sgdamore@opensolaris.org 		dnetp->disallowed_media |=
6309860Sgdamore@opensolaris.org 		    (1UL<<MEDIA_SYM_SCR) | (1UL<<MEDIA_SYM_SCR_FD);
6319860Sgdamore@opensolaris.org 	}
6329860Sgdamore@opensolaris.org 
6339860Sgdamore@opensolaris.org 	if (dnetp->full_duplex == 1) {
6349860Sgdamore@opensolaris.org 		dnetp->disallowed_media |=
6359860Sgdamore@opensolaris.org 		    (1UL<<MEDIA_TP) | (1UL<<MEDIA_SYM_SCR);
6369860Sgdamore@opensolaris.org 	} else if (dnetp->full_duplex == 0) {
6379860Sgdamore@opensolaris.org 		dnetp->disallowed_media |=
6389860Sgdamore@opensolaris.org 		    (1UL<<MEDIA_TP_FD) | (1UL<<MEDIA_SYM_SCR_FD);
6399860Sgdamore@opensolaris.org 	}
6409860Sgdamore@opensolaris.org 
6419860Sgdamore@opensolaris.org 	if (dnetp->bnc_indicator == 0) /* Disable BNC and AUI media */
6429860Sgdamore@opensolaris.org 		dnetp->disallowed_media |= (1UL<<MEDIA_BNC) | (1UL<<MEDIA_AUI);
6439860Sgdamore@opensolaris.org 	else if (dnetp->bnc_indicator == 1) /* Force BNC only */
6449860Sgdamore@opensolaris.org 		dnetp->disallowed_media =  (uint32_t)~(1U<<MEDIA_BNC);
6459860Sgdamore@opensolaris.org 	else if (dnetp->bnc_indicator == 2) /* Force AUI only */
6469860Sgdamore@opensolaris.org 		dnetp->disallowed_media = (uint32_t)~(1U<<MEDIA_AUI);
6479860Sgdamore@opensolaris.org 
64810340Sstallion@opensolaris.org 	dnet_reset_board(dnetp);
6499860Sgdamore@opensolaris.org 
6509860Sgdamore@opensolaris.org 	secondary = dnet_read_srom(devinfo, dnetp->board_type, dnetp->io_handle,
6519860Sgdamore@opensolaris.org 	    dnetp->io_reg, vendor_info, sizeof (vendor_info));
6529860Sgdamore@opensolaris.org 
6539860Sgdamore@opensolaris.org 	if (secondary == -1) /* ASSERT (vendor_info not big enough) */
6549860Sgdamore@opensolaris.org 		goto fail1;
6559860Sgdamore@opensolaris.org 
6569860Sgdamore@opensolaris.org 	dnet_parse_srom(dnetp, &dnetp->sr, vendor_info);
6579860Sgdamore@opensolaris.org 
6589860Sgdamore@opensolaris.org 	if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
6599860Sgdamore@opensolaris.org 	    printsrom_propname, 0))
6609860Sgdamore@opensolaris.org 		dnet_print_srom(&dnetp->sr);
6619860Sgdamore@opensolaris.org 
6629860Sgdamore@opensolaris.org 	dnetp->sr.netaddr[ETHERADDRL-1] += secondary;	/* unique ether addr */
6639860Sgdamore@opensolaris.org 
6649860Sgdamore@opensolaris.org 	BCOPY((caddr_t)dnetp->sr.netaddr,
6659860Sgdamore@opensolaris.org 	    (caddr_t)dnetp->vendor_addr, ETHERADDRL);
6669860Sgdamore@opensolaris.org 
6679860Sgdamore@opensolaris.org 	BCOPY((caddr_t)dnetp->sr.netaddr,
6689860Sgdamore@opensolaris.org 	    (caddr_t)dnetp->curr_macaddr, ETHERADDRL);
6699860Sgdamore@opensolaris.org 
6709860Sgdamore@opensolaris.org 	/*
6719860Sgdamore@opensolaris.org 	 * determine whether to implement workaround from DEC
6729860Sgdamore@opensolaris.org 	 * for DMA overrun errata.
6739860Sgdamore@opensolaris.org 	 */
6749860Sgdamore@opensolaris.org 	dnetp->overrun_workaround =
6759860Sgdamore@opensolaris.org 	    ((dnetp->board_type == DEVICE_ID_21140 && revid >= 0x20) ||
6769860Sgdamore@opensolaris.org 	    (dnetp->board_type == DEVICE_ID_21143 && revid <= 0x30)) ? 1 : 0;
6779860Sgdamore@opensolaris.org 
6789860Sgdamore@opensolaris.org 	dnetp->overrun_workaround =
6799860Sgdamore@opensolaris.org 	    ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
6809860Sgdamore@opensolaris.org 	    ofloprob_propname, dnetp->overrun_workaround);
6819860Sgdamore@opensolaris.org 
6829860Sgdamore@opensolaris.org 	/*
6839860Sgdamore@opensolaris.org 	 * Add the interrupt handler if dnet_hack_interrupts() returns 0.
6849860Sgdamore@opensolaris.org 	 * Otherwise dnet_hack_interrupts() itself adds the handler.
6859860Sgdamore@opensolaris.org 	 */
68610340Sstallion@opensolaris.org 	if (!dnet_hack_interrupts(dnetp, secondary)) {
6879860Sgdamore@opensolaris.org 		(void) ddi_add_intr(devinfo, 0, NULL,
68810340Sstallion@opensolaris.org 		    NULL, dnet_intr, (caddr_t)dnetp);
6899860Sgdamore@opensolaris.org 	}
6909860Sgdamore@opensolaris.org 
6919860Sgdamore@opensolaris.org 	dnetp->max_tx_desc = max_tx_desc;
6929860Sgdamore@opensolaris.org 	dnetp->max_rx_desc = max_rx_desc_21040;
6939860Sgdamore@opensolaris.org 	if (dnetp->board_type != DEVICE_ID_21040 &&
6949860Sgdamore@opensolaris.org 	    dnetp->board_type != DEVICE_ID_21041 &&
6959860Sgdamore@opensolaris.org 	    dnetp->speed != 10)
6969860Sgdamore@opensolaris.org 		dnetp->max_rx_desc = max_rx_desc_21140;
6979860Sgdamore@opensolaris.org 
6989860Sgdamore@opensolaris.org 	/* Allocate the TX and RX descriptors/buffers. */
69910340Sstallion@opensolaris.org 	if (dnet_alloc_bufs(dnetp) == FAILURE) {
7009860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "DNET: Not enough DMA memory for buffers.");
7019860Sgdamore@opensolaris.org 		goto fail2;
7029860Sgdamore@opensolaris.org 	}
7039860Sgdamore@opensolaris.org 
7049860Sgdamore@opensolaris.org 	/*
70510340Sstallion@opensolaris.org 	 *	Register ourselves with the GLDv3 interface
7069860Sgdamore@opensolaris.org 	 */
70710340Sstallion@opensolaris.org 	if ((macp = mac_alloc(MAC_VERSION)) == NULL)
70810340Sstallion@opensolaris.org 		goto fail2;
70910340Sstallion@opensolaris.org 
71010340Sstallion@opensolaris.org 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
71110340Sstallion@opensolaris.org 	macp->m_driver = dnetp;
71210340Sstallion@opensolaris.org 	macp->m_dip = devinfo;
71310340Sstallion@opensolaris.org 	macp->m_src_addr = dnetp->curr_macaddr;
71410340Sstallion@opensolaris.org 	macp->m_callbacks = &dnet_m_callbacks;
71510340Sstallion@opensolaris.org 	macp->m_min_sdu = 0;
71610340Sstallion@opensolaris.org 	macp->m_max_sdu = ETHERMTU;
71710340Sstallion@opensolaris.org 	macp->m_margin = VLAN_TAGSZ;
71810340Sstallion@opensolaris.org 
71910340Sstallion@opensolaris.org 	if (mac_register(macp, &dnetp->mac_handle) == 0) {
72010340Sstallion@opensolaris.org 		mac_free(macp);
72110340Sstallion@opensolaris.org 
7229860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->intrlock);
72310340Sstallion@opensolaris.org 
7249860Sgdamore@opensolaris.org 		dnetp->phyaddr = -1;
7259860Sgdamore@opensolaris.org 		if (dnetp->board_type == DEVICE_ID_21140 ||
7269860Sgdamore@opensolaris.org 		    dnetp->board_type == DEVICE_ID_21143)
72710340Sstallion@opensolaris.org 			do_phy(dnetp);	/* Initialize the PHY, if any */
72810340Sstallion@opensolaris.org 		find_active_media(dnetp);
7299860Sgdamore@opensolaris.org 
7309860Sgdamore@opensolaris.org 		/* if the chosen media is non-MII, stop the port monitor */
7319860Sgdamore@opensolaris.org 		if (dnetp->selected_media_block->media_code != MEDIA_MII &&
7329860Sgdamore@opensolaris.org 		    dnetp->mii != NULL) {
7339860Sgdamore@opensolaris.org 			mii_destroy(dnetp->mii);
7349860Sgdamore@opensolaris.org 			dnetp->mii = NULL;
7359860Sgdamore@opensolaris.org 			dnetp->phyaddr = -1;
7369860Sgdamore@opensolaris.org 		}
7379860Sgdamore@opensolaris.org 
7389860Sgdamore@opensolaris.org #ifdef DNETDEBUG
7399860Sgdamore@opensolaris.org 		if (dnetdebug & DNETSENSE)
7409860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "dnet: link configured : %s",
7419860Sgdamore@opensolaris.org 			    media_str[dnetp->selected_media_block->media_code]);
7429860Sgdamore@opensolaris.org #endif
7439860Sgdamore@opensolaris.org 		bzero(dnetp->setup_buf_vaddr, SETUPBUF_SIZE);
74410340Sstallion@opensolaris.org 
74510340Sstallion@opensolaris.org 		dnet_reset_board(dnetp);
74610340Sstallion@opensolaris.org 		dnet_init_board(dnetp);
74710340Sstallion@opensolaris.org 
7489860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
74910340Sstallion@opensolaris.org 
75010340Sstallion@opensolaris.org 		(void) dnet_m_unicst(dnetp, dnetp->curr_macaddr);
75110340Sstallion@opensolaris.org 		(void) dnet_m_multicst(dnetp, B_TRUE, dnet_broadcastaddr);
75210340Sstallion@opensolaris.org 
7539860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
7549860Sgdamore@opensolaris.org 	}
75510340Sstallion@opensolaris.org 
75610340Sstallion@opensolaris.org 	mac_free(macp);
7579860Sgdamore@opensolaris.org fail2:
7589860Sgdamore@opensolaris.org 	/* XXX function return value ignored */
7599860Sgdamore@opensolaris.org 	/*
7609860Sgdamore@opensolaris.org 	 * dnet_detach_hacked_interrupt() will remove
7619860Sgdamore@opensolaris.org 	 * interrupt for the non-hacked case also.
7629860Sgdamore@opensolaris.org 	 */
7639860Sgdamore@opensolaris.org 	(void) dnet_detach_hacked_interrupt(devinfo);
76410340Sstallion@opensolaris.org 	dnet_free_bufs(dnetp);
7659860Sgdamore@opensolaris.org fail1:
7669860Sgdamore@opensolaris.org 	mutex_destroy(&dnetp->txlock);
7679860Sgdamore@opensolaris.org 	mutex_destroy(&dnetp->intrlock);
7689860Sgdamore@opensolaris.org fail:
7699860Sgdamore@opensolaris.org 	ddi_regs_map_free(&dnetp->io_handle);
7709860Sgdamore@opensolaris.org 	kmem_free(dnetp, sizeof (struct dnetinstance));
7719860Sgdamore@opensolaris.org 	return (DDI_FAILURE);
7729860Sgdamore@opensolaris.org }
7739860Sgdamore@opensolaris.org 
7749860Sgdamore@opensolaris.org /*
7759860Sgdamore@opensolaris.org  * detach(9E) -- Detach a device from the system
7769860Sgdamore@opensolaris.org  */
7779860Sgdamore@opensolaris.org static int
dnet_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)77810340Sstallion@opensolaris.org dnet_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
7799860Sgdamore@opensolaris.org {
7809860Sgdamore@opensolaris.org 	int32_t rc;
7819860Sgdamore@opensolaris.org 	struct dnetinstance *dnetp;		/* Our private device info */
7829860Sgdamore@opensolaris.org 	int32_t		proplen;
7839860Sgdamore@opensolaris.org 
78410340Sstallion@opensolaris.org 	/* Get the driver private (dnetinstance) structure */
78510340Sstallion@opensolaris.org 	dnetp = ddi_get_driver_private(devinfo);
7869860Sgdamore@opensolaris.org 
7879860Sgdamore@opensolaris.org 	switch (cmd) {
7889860Sgdamore@opensolaris.org 	case DDI_DETACH:
7899860Sgdamore@opensolaris.org 		break;
7909860Sgdamore@opensolaris.org 
7919860Sgdamore@opensolaris.org 	case DDI_SUSPEND:
7929860Sgdamore@opensolaris.org 		/*
7939860Sgdamore@opensolaris.org 		 * NB: dnetp->suspended can only be modified (marked true)
7949860Sgdamore@opensolaris.org 		 * if both intrlock and txlock are held.  This keeps both
7959860Sgdamore@opensolaris.org 		 * tx and rx code paths excluded.
7969860Sgdamore@opensolaris.org 		 */
7979860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->intrlock);
7989860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->txlock);
7999860Sgdamore@opensolaris.org 		dnetp->suspended = B_TRUE;
80010340Sstallion@opensolaris.org 		dnet_reset_board(dnetp);
8019860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
8029860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
8039860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
8049860Sgdamore@opensolaris.org 
8059860Sgdamore@opensolaris.org 	default:
8069860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
8079860Sgdamore@opensolaris.org 	}
8089860Sgdamore@opensolaris.org 
8099860Sgdamore@opensolaris.org 	/*
81010340Sstallion@opensolaris.org 	 *	Unregister ourselves from the GLDv3 interface
8119860Sgdamore@opensolaris.org 	 */
81210340Sstallion@opensolaris.org 	if (mac_unregister(dnetp->mac_handle) != 0)
8139860Sgdamore@opensolaris.org 		return (DDI_FAILURE);
8149860Sgdamore@opensolaris.org 
8159860Sgdamore@opensolaris.org 	/* stop the board if it is running */
81610340Sstallion@opensolaris.org 	dnet_reset_board(dnetp);
8179860Sgdamore@opensolaris.org 
8189860Sgdamore@opensolaris.org 	if ((rc = dnet_detach_hacked_interrupt(devinfo)) != DDI_SUCCESS)
8199860Sgdamore@opensolaris.org 		return (rc);
8209860Sgdamore@opensolaris.org 
8219860Sgdamore@opensolaris.org 	if (dnetp->mii != NULL)
8229860Sgdamore@opensolaris.org 		mii_destroy(dnetp->mii);
8239860Sgdamore@opensolaris.org 
8249860Sgdamore@opensolaris.org 	/* Free leaf information */
8259860Sgdamore@opensolaris.org 	set_leaf(&dnetp->sr, NULL);
8269860Sgdamore@opensolaris.org 
8279860Sgdamore@opensolaris.org 	ddi_regs_map_free(&dnetp->io_handle);
82810340Sstallion@opensolaris.org 	dnet_free_bufs(dnetp);
8299860Sgdamore@opensolaris.org 	mutex_destroy(&dnetp->txlock);
8309860Sgdamore@opensolaris.org 	mutex_destroy(&dnetp->intrlock);
8319860Sgdamore@opensolaris.org 	kmem_free(dnetp, sizeof (struct dnetinstance));
8329860Sgdamore@opensolaris.org 
8339860Sgdamore@opensolaris.org #ifdef BUG_4010796
8349860Sgdamore@opensolaris.org 	if (ddi_getproplen(DDI_DEV_T_ANY, devinfo, 0,
8359860Sgdamore@opensolaris.org 	    "DNET_HACK", &proplen) != DDI_PROP_SUCCESS)
8369860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
8379860Sgdamore@opensolaris.org 
8389860Sgdamore@opensolaris.org 	/*
8399860Sgdamore@opensolaris.org 	 * We must remove the properties we added, because if we leave
8409860Sgdamore@opensolaris.org 	 * them in the devinfo nodes and the driver is unloaded, when
8419860Sgdamore@opensolaris.org 	 * the driver is reloaded the info will still be there, causing
8429860Sgdamore@opensolaris.org 	 * nodes which had returned PROBE_PARTIAL the first time to
8439860Sgdamore@opensolaris.org 	 * instead return PROBE_SUCCESS, in turn causing the nodes to be
8449860Sgdamore@opensolaris.org 	 * attached in a different order, causing their PPA numbers to
8459860Sgdamore@opensolaris.org 	 * be different the second time around, which is undesirable.
8469860Sgdamore@opensolaris.org 	 */
8479860Sgdamore@opensolaris.org 	(void) ddi_prop_remove(DDI_DEV_T_NONE, devinfo, "DNET_HACK");
8489860Sgdamore@opensolaris.org 	(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
8499860Sgdamore@opensolaris.org 	    "DNET_SROM");
8509860Sgdamore@opensolaris.org 	(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
8519860Sgdamore@opensolaris.org 	    "DNET_DEVNUM");
8529860Sgdamore@opensolaris.org #endif
8539860Sgdamore@opensolaris.org 
8549860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
8559860Sgdamore@opensolaris.org }
8569860Sgdamore@opensolaris.org 
85710340Sstallion@opensolaris.org int
dnet_quiesce(dev_info_t * dip)85810340Sstallion@opensolaris.org dnet_quiesce(dev_info_t *dip)
8599860Sgdamore@opensolaris.org {
86010340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = ddi_get_driver_private(dip);
86110340Sstallion@opensolaris.org 
8629860Sgdamore@opensolaris.org 	/*
86310340Sstallion@opensolaris.org 	 * Reset chip (disables interrupts).
8649860Sgdamore@opensolaris.org 	 */
86510340Sstallion@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, INT_MASK_REG), 0);
86610340Sstallion@opensolaris.org 	ddi_put32(dnetp->io_handle,
86710340Sstallion@opensolaris.org 	    REG32(dnetp->io_reg, BUS_MODE_REG), SW_RESET);
86810340Sstallion@opensolaris.org 
86910340Sstallion@opensolaris.org 	return (DDI_SUCCESS);
8709860Sgdamore@opensolaris.org }
8719860Sgdamore@opensolaris.org 
8729860Sgdamore@opensolaris.org static void
dnet_reset_board(struct dnetinstance * dnetp)87310340Sstallion@opensolaris.org dnet_reset_board(struct dnetinstance *dnetp)
8749860Sgdamore@opensolaris.org {
8759860Sgdamore@opensolaris.org 	uint32_t	val;
8769860Sgdamore@opensolaris.org 
8779860Sgdamore@opensolaris.org 	/*
8789860Sgdamore@opensolaris.org 	 * before initializing the dnet should be in STOP state
8799860Sgdamore@opensolaris.org 	 */
8809860Sgdamore@opensolaris.org 	val = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG));
8819860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
8829860Sgdamore@opensolaris.org 	    val & ~(START_TRANSMIT | START_RECEIVE));
8839860Sgdamore@opensolaris.org 
8849860Sgdamore@opensolaris.org 	/*
8859860Sgdamore@opensolaris.org 	 * Reset the chip
8869860Sgdamore@opensolaris.org 	 */
8879860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, INT_MASK_REG), 0);
8889860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle,
8899860Sgdamore@opensolaris.org 	    REG32(dnetp->io_reg, BUS_MODE_REG), SW_RESET);
8909860Sgdamore@opensolaris.org 	drv_usecwait(5);
8919860Sgdamore@opensolaris.org }
8929860Sgdamore@opensolaris.org 
8939860Sgdamore@opensolaris.org /*
8949860Sgdamore@opensolaris.org  * dnet_init_board() -- initialize the specified network board short of
8959860Sgdamore@opensolaris.org  * actually starting the board.  Call after dnet_reset_board().
8969860Sgdamore@opensolaris.org  * called with intrlock held.
8979860Sgdamore@opensolaris.org  */
8989860Sgdamore@opensolaris.org static void
dnet_init_board(struct dnetinstance * dnetp)89910340Sstallion@opensolaris.org dnet_init_board(struct dnetinstance *dnetp)
9009860Sgdamore@opensolaris.org {
90110340Sstallion@opensolaris.org 	set_opr(dnetp);
90210340Sstallion@opensolaris.org 	set_gpr(dnetp);
90310340Sstallion@opensolaris.org 	set_sia(dnetp);
90410340Sstallion@opensolaris.org 	dnet_chip_init(dnetp);
9059860Sgdamore@opensolaris.org }
9069860Sgdamore@opensolaris.org 
9079860Sgdamore@opensolaris.org /* dnet_chip_init() - called with intrlock held */
9089860Sgdamore@opensolaris.org static void
dnet_chip_init(struct dnetinstance * dnetp)90910340Sstallion@opensolaris.org dnet_chip_init(struct dnetinstance *dnetp)
9109860Sgdamore@opensolaris.org {
9119860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, BUS_MODE_REG),
9129860Sgdamore@opensolaris.org 	    CACHE_ALIGN | BURST_SIZE);		/* CSR0 */
9139860Sgdamore@opensolaris.org 
9149860Sgdamore@opensolaris.org 	/*
9159860Sgdamore@opensolaris.org 	 * Initialize the TX and RX descriptors/buffers
9169860Sgdamore@opensolaris.org 	 */
91710340Sstallion@opensolaris.org 	dnet_init_txrx_bufs(dnetp);
9189860Sgdamore@opensolaris.org 
9199860Sgdamore@opensolaris.org 	/*
9209860Sgdamore@opensolaris.org 	 * Set the base address of the Rx descriptor list in CSR3
9219860Sgdamore@opensolaris.org 	 */
9229860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, RX_BASE_ADDR_REG),
9239860Sgdamore@opensolaris.org 	    dnetp->rx_desc_paddr);
9249860Sgdamore@opensolaris.org 
9259860Sgdamore@opensolaris.org 	/*
9269860Sgdamore@opensolaris.org 	 * Set the base address of the Tx descrptor list in CSR4
9279860Sgdamore@opensolaris.org 	 */
9289860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, TX_BASE_ADDR_REG),
9299860Sgdamore@opensolaris.org 	    dnetp->tx_desc_paddr);
9309860Sgdamore@opensolaris.org 
9319860Sgdamore@opensolaris.org 	dnetp->tx_current_desc = dnetp->rx_current_desc = 0;
9329860Sgdamore@opensolaris.org 	dnetp->transmitted_desc = 0;
9339860Sgdamore@opensolaris.org 	dnetp->free_desc = dnetp->max_tx_desc;
93410340Sstallion@opensolaris.org 	enable_interrupts(dnetp);
9359860Sgdamore@opensolaris.org }
9369860Sgdamore@opensolaris.org 
9379860Sgdamore@opensolaris.org /*
9389860Sgdamore@opensolaris.org  *	dnet_start() -- start the board receiving and allow transmits.
9399860Sgdamore@opensolaris.org  *  Called with intrlock held.
9409860Sgdamore@opensolaris.org  */
9419860Sgdamore@opensolaris.org static int
dnet_start(struct dnetinstance * dnetp)94210340Sstallion@opensolaris.org dnet_start(struct dnetinstance *dnetp)
9439860Sgdamore@opensolaris.org {
94410340Sstallion@opensolaris.org 	uint32_t val;
9459860Sgdamore@opensolaris.org 
9469860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
9479860Sgdamore@opensolaris.org 	/*
9489860Sgdamore@opensolaris.org 	 * start the board and enable receiving
9499860Sgdamore@opensolaris.org 	 */
9509860Sgdamore@opensolaris.org 	val = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG));
9519860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
9529860Sgdamore@opensolaris.org 	    val | START_TRANSMIT);
95310340Sstallion@opensolaris.org 	(void) dnet_set_addr(dnetp);
9549860Sgdamore@opensolaris.org 	val = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG));
9559860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
9569860Sgdamore@opensolaris.org 	    val | START_RECEIVE);
95710340Sstallion@opensolaris.org 	enable_interrupts(dnetp);
9589860Sgdamore@opensolaris.org 	return (0);
9599860Sgdamore@opensolaris.org }
9609860Sgdamore@opensolaris.org 
9619860Sgdamore@opensolaris.org static int
dnet_m_start(void * arg)96210340Sstallion@opensolaris.org dnet_m_start(void *arg)
9639860Sgdamore@opensolaris.org {
96410340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
9659860Sgdamore@opensolaris.org 
9669860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
9679860Sgdamore@opensolaris.org 	dnetp->running = B_TRUE;
9689860Sgdamore@opensolaris.org 	/*
9699860Sgdamore@opensolaris.org 	 * start the board and enable receiving
9709860Sgdamore@opensolaris.org 	 */
9719860Sgdamore@opensolaris.org 	if (!dnetp->suspended)
97210340Sstallion@opensolaris.org 		(void) dnet_start(dnetp);
9739860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
9749860Sgdamore@opensolaris.org 	return (0);
9759860Sgdamore@opensolaris.org }
9769860Sgdamore@opensolaris.org 
97710340Sstallion@opensolaris.org static void
dnet_m_stop(void * arg)97810340Sstallion@opensolaris.org dnet_m_stop(void *arg)
9799860Sgdamore@opensolaris.org {
98010340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
98110340Sstallion@opensolaris.org 	uint32_t val;
98210340Sstallion@opensolaris.org 
9839860Sgdamore@opensolaris.org 	/*
9849860Sgdamore@opensolaris.org 	 * stop the board and disable transmit/receive
9859860Sgdamore@opensolaris.org 	 */
9869860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
9879860Sgdamore@opensolaris.org 	if (!dnetp->suspended) {
9889860Sgdamore@opensolaris.org 		val = ddi_get32(dnetp->io_handle,
9899860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, OPN_MODE_REG));
9909860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
9919860Sgdamore@opensolaris.org 		    val & ~(START_TRANSMIT | START_RECEIVE));
9929860Sgdamore@opensolaris.org 	}
99311198Sstallion@opensolaris.org 	mac_link_update(dnetp->mac_handle, LINK_STATE_UNKNOWN);
9949860Sgdamore@opensolaris.org 	dnetp->running = B_FALSE;
9959860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
9969860Sgdamore@opensolaris.org }
9979860Sgdamore@opensolaris.org 
9989860Sgdamore@opensolaris.org /*
9999860Sgdamore@opensolaris.org  *	dnet_set_addr() -- set the physical network address on the board
10009860Sgdamore@opensolaris.org  *  Called with intrlock held.
10019860Sgdamore@opensolaris.org  */
10029860Sgdamore@opensolaris.org static int
dnet_set_addr(struct dnetinstance * dnetp)100310340Sstallion@opensolaris.org dnet_set_addr(struct dnetinstance *dnetp)
10049860Sgdamore@opensolaris.org {
10059860Sgdamore@opensolaris.org 	struct tx_desc_type *desc;
10069860Sgdamore@opensolaris.org 	int 		current_desc;
10079860Sgdamore@opensolaris.org 	uint32_t	val;
10089860Sgdamore@opensolaris.org 
10099860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
10109860Sgdamore@opensolaris.org 
10119860Sgdamore@opensolaris.org 	val = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG));
10129860Sgdamore@opensolaris.org 	if (!(val & START_TRANSMIT))
10139860Sgdamore@opensolaris.org 		return (0);
10149860Sgdamore@opensolaris.org 
10159860Sgdamore@opensolaris.org 	current_desc = dnetp->tx_current_desc;
10169860Sgdamore@opensolaris.org 	desc = &dnetp->tx_desc[current_desc];
10179860Sgdamore@opensolaris.org 
10189860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->txlock);
10199860Sgdamore@opensolaris.org 	dnetp->need_saddr = 0;
10209860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->txlock);
10219860Sgdamore@opensolaris.org 
102210340Sstallion@opensolaris.org 	if ((alloc_descriptor(dnetp)) == FAILURE) {
10239860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->txlock);
10249860Sgdamore@opensolaris.org 		dnetp->need_saddr = 1;
10259860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
10269860Sgdamore@opensolaris.org #ifdef DNETDEBUG
10279860Sgdamore@opensolaris.org 		if (dnetdebug & DNETTRACE)
10289860Sgdamore@opensolaris.org 			cmn_err(CE_WARN, "DNET saddr:alloc descriptor failure");
10299860Sgdamore@opensolaris.org #endif
10309860Sgdamore@opensolaris.org 		return (0);
10319860Sgdamore@opensolaris.org 	}
10329860Sgdamore@opensolaris.org 
10339860Sgdamore@opensolaris.org 	desc->buffer1			= dnetp->setup_buf_paddr;
10349860Sgdamore@opensolaris.org 	desc->buffer2			= 0;
10359860Sgdamore@opensolaris.org 	desc->desc1.buffer_size1 	= SETUPBUF_SIZE;
10369860Sgdamore@opensolaris.org 	desc->desc1.buffer_size2 	= 0;
10379860Sgdamore@opensolaris.org 	desc->desc1.setup_packet	= 1;
10389860Sgdamore@opensolaris.org 	desc->desc1.first_desc		= 0;
10399860Sgdamore@opensolaris.org 	desc->desc1.last_desc 		= 0;
10409860Sgdamore@opensolaris.org 	desc->desc1.filter_type0 	= 1;
10419860Sgdamore@opensolaris.org 	desc->desc1.filter_type1 	= 1;
10429860Sgdamore@opensolaris.org 	desc->desc1.int_on_comp		= 1;
10439860Sgdamore@opensolaris.org 
10449860Sgdamore@opensolaris.org 	desc->desc0.own = 1;
10459860Sgdamore@opensolaris.org 	ddi_put8(dnetp->io_handle, REG8(dnetp->io_reg, TX_POLL_REG),
10469860Sgdamore@opensolaris.org 	    TX_POLL_DEMAND);
10479860Sgdamore@opensolaris.org 	return (0);
10489860Sgdamore@opensolaris.org }
10499860Sgdamore@opensolaris.org 
10509860Sgdamore@opensolaris.org static int
dnet_m_unicst(void * arg,const uint8_t * macaddr)105110340Sstallion@opensolaris.org dnet_m_unicst(void *arg, const uint8_t *macaddr)
10529860Sgdamore@opensolaris.org {
105310340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
10549860Sgdamore@opensolaris.org 	uint32_t	index;
10559860Sgdamore@opensolaris.org 	uint32_t	*hashp;
10569860Sgdamore@opensolaris.org 
10579860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
10589860Sgdamore@opensolaris.org 
10599860Sgdamore@opensolaris.org 	bcopy(macaddr, dnetp->curr_macaddr, ETHERADDRL);
10609860Sgdamore@opensolaris.org 
10619860Sgdamore@opensolaris.org 	/*
10629860Sgdamore@opensolaris.org 	 * As we are using Imperfect filtering, the broadcast address has to
10639860Sgdamore@opensolaris.org 	 * be set explicitly in the 512 bit hash table.  Hence the index into
10649860Sgdamore@opensolaris.org 	 * the hash table is calculated and the bit set to enable reception
10659860Sgdamore@opensolaris.org 	 * of broadcast packets.
10669860Sgdamore@opensolaris.org 	 *
10679860Sgdamore@opensolaris.org 	 * We also use HASH_ONLY mode, without using the perfect filter for
10689860Sgdamore@opensolaris.org 	 * our station address, because there appears to be a bug in the
10699860Sgdamore@opensolaris.org 	 * 21140 where it fails to receive the specified perfect filter
10709860Sgdamore@opensolaris.org 	 * address.
10719860Sgdamore@opensolaris.org 	 *
10729860Sgdamore@opensolaris.org 	 * Since dlsdmult comes through here, it doesn't matter that the count
10739860Sgdamore@opensolaris.org 	 * is wrong for the two bits that correspond to the cases below. The
10749860Sgdamore@opensolaris.org 	 * worst that could happen is that we'd leave on a bit for an old
10759860Sgdamore@opensolaris.org 	 * macaddr, in the case where the macaddr gets changed, which is rare.
10769860Sgdamore@opensolaris.org 	 * Since filtering is imperfect, it is OK if that happens.
10779860Sgdamore@opensolaris.org 	 */
10789860Sgdamore@opensolaris.org 	hashp = (uint32_t *)dnetp->setup_buf_vaddr;
107910340Sstallion@opensolaris.org 	index = hashindex((uint8_t *)dnet_broadcastaddr);
10809860Sgdamore@opensolaris.org 	hashp[ index / 16 ] |= 1 << (index % 16);
10819860Sgdamore@opensolaris.org 
108210340Sstallion@opensolaris.org 	index = hashindex((uint8_t *)dnetp->curr_macaddr);
10839860Sgdamore@opensolaris.org 	hashp[ index / 16 ] |= 1 << (index % 16);
10849860Sgdamore@opensolaris.org 
10859860Sgdamore@opensolaris.org 	if (!dnetp->suspended)
108610340Sstallion@opensolaris.org 		(void) dnet_set_addr(dnetp);
10879860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
10889860Sgdamore@opensolaris.org 	return (0);
10899860Sgdamore@opensolaris.org }
10909860Sgdamore@opensolaris.org 
10919860Sgdamore@opensolaris.org static int
dnet_m_multicst(void * arg,boolean_t add,const uint8_t * macaddr)109210340Sstallion@opensolaris.org dnet_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
10939860Sgdamore@opensolaris.org {
109410340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
10959860Sgdamore@opensolaris.org 	uint32_t	index;
10969860Sgdamore@opensolaris.org 	uint32_t	*hashp;
10979860Sgdamore@opensolaris.org 	uint32_t	retval;
10989860Sgdamore@opensolaris.org 
10999860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
110010340Sstallion@opensolaris.org 	index = hashindex(macaddr);
11019860Sgdamore@opensolaris.org 	hashp = (uint32_t *)dnetp->setup_buf_vaddr;
110210340Sstallion@opensolaris.org 	if (add) {
11039860Sgdamore@opensolaris.org 		if (dnetp->multicast_cnt[index]++) {
11049860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->intrlock);
11059860Sgdamore@opensolaris.org 			return (0);
11069860Sgdamore@opensolaris.org 		}
11079860Sgdamore@opensolaris.org 		hashp[ index / 16 ] |= 1 << (index % 16);
11089860Sgdamore@opensolaris.org 	} else {
11099860Sgdamore@opensolaris.org 		if (--dnetp->multicast_cnt[index]) {
11109860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->intrlock);
11119860Sgdamore@opensolaris.org 			return (0);
11129860Sgdamore@opensolaris.org 		}
11139860Sgdamore@opensolaris.org 		hashp[ index / 16 ] &= ~ (1 << (index % 16));
11149860Sgdamore@opensolaris.org 	}
11159860Sgdamore@opensolaris.org 	if (!dnetp->suspended)
111610340Sstallion@opensolaris.org 		retval = dnet_set_addr(dnetp);
11179860Sgdamore@opensolaris.org 	else
11189860Sgdamore@opensolaris.org 		retval = 0;
11199860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
11209860Sgdamore@opensolaris.org 	return (retval);
11219860Sgdamore@opensolaris.org }
11229860Sgdamore@opensolaris.org 
11239860Sgdamore@opensolaris.org /*
11249860Sgdamore@opensolaris.org  * A hashing function used for setting the
11259860Sgdamore@opensolaris.org  * node address or a multicast address
11269860Sgdamore@opensolaris.org  */
11279860Sgdamore@opensolaris.org static uint32_t
hashindex(const uint8_t * address)112810340Sstallion@opensolaris.org hashindex(const uint8_t *address)
11299860Sgdamore@opensolaris.org {
11309860Sgdamore@opensolaris.org 	uint32_t	crc = (uint32_t)HASH_CRC;
11319860Sgdamore@opensolaris.org 	uint32_t const 	POLY = HASH_POLY;
11329860Sgdamore@opensolaris.org 	uint32_t	msb;
11339860Sgdamore@opensolaris.org 	int32_t 	byteslength;
11349860Sgdamore@opensolaris.org 	uint8_t 	currentbyte;
11359860Sgdamore@opensolaris.org 	uint32_t 	index;
11369860Sgdamore@opensolaris.org 	int32_t 	bit;
11379860Sgdamore@opensolaris.org 	int32_t		shift;
11389860Sgdamore@opensolaris.org 
11399860Sgdamore@opensolaris.org 	for (byteslength = 0; byteslength < ETHERADDRL; byteslength++) {
11409860Sgdamore@opensolaris.org 		currentbyte = address[byteslength];
11419860Sgdamore@opensolaris.org 		for (bit = 0; bit < 8; bit++) {
11429860Sgdamore@opensolaris.org 			msb = crc >> 31;
11439860Sgdamore@opensolaris.org 			crc <<= 1;
11449860Sgdamore@opensolaris.org 			if (msb ^ (currentbyte & 1)) {
11459860Sgdamore@opensolaris.org 				crc ^= POLY;
11469860Sgdamore@opensolaris.org 				crc |= 0x00000001;
11479860Sgdamore@opensolaris.org 			}
11489860Sgdamore@opensolaris.org 			currentbyte >>= 1;
11499860Sgdamore@opensolaris.org 		}
11509860Sgdamore@opensolaris.org 	}
11519860Sgdamore@opensolaris.org 
11529860Sgdamore@opensolaris.org 	for (index = 0, bit = 23, shift = 8; shift >= 0; bit++, shift--) {
11539860Sgdamore@opensolaris.org 		index |= (((crc >> bit) & 1) << shift);
11549860Sgdamore@opensolaris.org 	}
11559860Sgdamore@opensolaris.org 	return (index);
11569860Sgdamore@opensolaris.org }
11579860Sgdamore@opensolaris.org 
11589860Sgdamore@opensolaris.org static int
dnet_m_setpromisc(void * arg,boolean_t on)115910340Sstallion@opensolaris.org dnet_m_setpromisc(void *arg, boolean_t on)
11609860Sgdamore@opensolaris.org {
116110340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
11629860Sgdamore@opensolaris.org 	uint32_t val;
11639860Sgdamore@opensolaris.org 
11649860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
11659860Sgdamore@opensolaris.org 	if (dnetp->promisc == on) {
11669860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
116710340Sstallion@opensolaris.org 		return (0);
11689860Sgdamore@opensolaris.org 	}
11699860Sgdamore@opensolaris.org 	dnetp->promisc = on;
11709860Sgdamore@opensolaris.org 
11719860Sgdamore@opensolaris.org 	if (!dnetp->suspended) {
11729860Sgdamore@opensolaris.org 		val = ddi_get32(dnetp->io_handle,
11739860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, OPN_MODE_REG));
117410340Sstallion@opensolaris.org 		if (on)
11759860Sgdamore@opensolaris.org 			ddi_put32(dnetp->io_handle,
11769860Sgdamore@opensolaris.org 			    REG32(dnetp->io_reg, OPN_MODE_REG),
11779860Sgdamore@opensolaris.org 			    val | PROM_MODE);
11789860Sgdamore@opensolaris.org 		else
11799860Sgdamore@opensolaris.org 			ddi_put32(dnetp->io_handle,
11809860Sgdamore@opensolaris.org 			    REG32(dnetp->io_reg, OPN_MODE_REG),
11819860Sgdamore@opensolaris.org 			    val & (~PROM_MODE));
11829860Sgdamore@opensolaris.org 	}
11839860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
118410340Sstallion@opensolaris.org 	return (0);
11859860Sgdamore@opensolaris.org }
11869860Sgdamore@opensolaris.org 
11879860Sgdamore@opensolaris.org static int
dnet_m_getstat(void * arg,uint_t stat,uint64_t * val)118810340Sstallion@opensolaris.org dnet_m_getstat(void *arg, uint_t stat, uint64_t *val)
11899860Sgdamore@opensolaris.org {
119010340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
119110340Sstallion@opensolaris.org 
119210340Sstallion@opensolaris.org 	switch (stat) {
119310340Sstallion@opensolaris.org 	case MAC_STAT_IFSPEED:
119411198Sstallion@opensolaris.org 		if (!dnetp->running) {
119511198Sstallion@opensolaris.org 			*val = 0;
119611198Sstallion@opensolaris.org 		} else {
119711198Sstallion@opensolaris.org 			*val = (dnetp->mii_up ?
119811198Sstallion@opensolaris.org 			    dnetp->mii_speed : dnetp->speed) * 1000000;
119911198Sstallion@opensolaris.org 		}
120010340Sstallion@opensolaris.org 		break;
120110340Sstallion@opensolaris.org 
120210340Sstallion@opensolaris.org 	case MAC_STAT_NORCVBUF:
120310340Sstallion@opensolaris.org 		*val = dnetp->stat_norcvbuf;
120410340Sstallion@opensolaris.org 		break;
120510340Sstallion@opensolaris.org 
120610340Sstallion@opensolaris.org 	case MAC_STAT_IERRORS:
120710340Sstallion@opensolaris.org 		*val = dnetp->stat_errrcv;
120810340Sstallion@opensolaris.org 		break;
120910340Sstallion@opensolaris.org 
121010340Sstallion@opensolaris.org 	case MAC_STAT_OERRORS:
121110340Sstallion@opensolaris.org 		*val = dnetp->stat_errxmt;
121210340Sstallion@opensolaris.org 		break;
121310340Sstallion@opensolaris.org 
121410340Sstallion@opensolaris.org 	case MAC_STAT_COLLISIONS:
121510340Sstallion@opensolaris.org 		*val = dnetp->stat_collisions;
121610340Sstallion@opensolaris.org 		break;
121710340Sstallion@opensolaris.org 
121810340Sstallion@opensolaris.org 	case ETHER_STAT_DEFER_XMTS:
121910340Sstallion@opensolaris.org 		*val = dnetp->stat_defer;
122010340Sstallion@opensolaris.org 		break;
122110340Sstallion@opensolaris.org 
122210340Sstallion@opensolaris.org 	case ETHER_STAT_CARRIER_ERRORS:
122310340Sstallion@opensolaris.org 		*val = dnetp->stat_nocarrier;
122410340Sstallion@opensolaris.org 		break;
122510340Sstallion@opensolaris.org 
122610340Sstallion@opensolaris.org 	case ETHER_STAT_TOOSHORT_ERRORS:
122710340Sstallion@opensolaris.org 		*val = dnetp->stat_short;
122810340Sstallion@opensolaris.org 		break;
122910340Sstallion@opensolaris.org 
123010340Sstallion@opensolaris.org 	case ETHER_STAT_LINK_DUPLEX:
123110340Sstallion@opensolaris.org 		if (!dnetp->running) {
123210340Sstallion@opensolaris.org 			*val = LINK_DUPLEX_UNKNOWN;
123311198Sstallion@opensolaris.org 
123411198Sstallion@opensolaris.org 		} else if (dnetp->mii_up) {
123510340Sstallion@opensolaris.org 			*val = dnetp->mii_duplex ?
123610340Sstallion@opensolaris.org 			    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
123710340Sstallion@opensolaris.org 		} else {
123810340Sstallion@opensolaris.org 			*val = dnetp->full_duplex ?
123910340Sstallion@opensolaris.org 			    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
124010340Sstallion@opensolaris.org 		}
124110340Sstallion@opensolaris.org 		break;
124210340Sstallion@opensolaris.org 
124310340Sstallion@opensolaris.org 	case ETHER_STAT_TX_LATE_COLLISIONS:
124410340Sstallion@opensolaris.org 		*val = dnetp->stat_xmtlatecoll;
124510340Sstallion@opensolaris.org 		break;
124610340Sstallion@opensolaris.org 
124710340Sstallion@opensolaris.org 	case ETHER_STAT_EX_COLLISIONS:
124810340Sstallion@opensolaris.org 		*val = dnetp->stat_excoll;
124910340Sstallion@opensolaris.org 		break;
125010340Sstallion@opensolaris.org 
125110340Sstallion@opensolaris.org 	case MAC_STAT_OVERFLOWS:
125210340Sstallion@opensolaris.org 		*val = dnetp->stat_overflow;
125310340Sstallion@opensolaris.org 		break;
125410340Sstallion@opensolaris.org 
125510340Sstallion@opensolaris.org 	case MAC_STAT_UNDERFLOWS:
125610340Sstallion@opensolaris.org 		*val = dnetp->stat_underflow;
125710340Sstallion@opensolaris.org 		break;
125810340Sstallion@opensolaris.org 
125910340Sstallion@opensolaris.org 	default:
126010340Sstallion@opensolaris.org 		return (ENOTSUP);
12619860Sgdamore@opensolaris.org 	}
12629860Sgdamore@opensolaris.org 
126310340Sstallion@opensolaris.org 	return (0);
12649860Sgdamore@opensolaris.org }
12659860Sgdamore@opensolaris.org 
12669860Sgdamore@opensolaris.org #define	NextTXIndex(index) (((index)+1) % dnetp->max_tx_desc)
12679860Sgdamore@opensolaris.org #define	PrevTXIndex(index) (((index)-1) < 0 ? dnetp->max_tx_desc - 1: (index)-1)
12689860Sgdamore@opensolaris.org 
126910340Sstallion@opensolaris.org static mblk_t *
dnet_m_tx(void * arg,mblk_t * mp)127010340Sstallion@opensolaris.org dnet_m_tx(void *arg, mblk_t *mp)
12719860Sgdamore@opensolaris.org {
127210340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = arg;
127310340Sstallion@opensolaris.org 
127410340Sstallion@opensolaris.org 	mutex_enter(&dnetp->txlock);
127510340Sstallion@opensolaris.org 
127610340Sstallion@opensolaris.org 	/* if suspended, drop the packet on the floor, we missed it */
127710340Sstallion@opensolaris.org 	if (dnetp->suspended) {
127810340Sstallion@opensolaris.org 		mutex_exit(&dnetp->txlock);
127910340Sstallion@opensolaris.org 		freemsg(mp);
128010340Sstallion@opensolaris.org 		return (NULL);
128110340Sstallion@opensolaris.org 	}
128210340Sstallion@opensolaris.org 
128310340Sstallion@opensolaris.org 	if (dnetp->need_saddr) {
128410340Sstallion@opensolaris.org 		/* XXX function return value ignored */
128510340Sstallion@opensolaris.org 		mutex_exit(&dnetp->txlock);
128610340Sstallion@opensolaris.org 		mutex_enter(&dnetp->intrlock);
128710340Sstallion@opensolaris.org 		(void) dnet_set_addr(dnetp);
128810340Sstallion@opensolaris.org 		mutex_exit(&dnetp->intrlock);
128910340Sstallion@opensolaris.org 		mutex_enter(&dnetp->txlock);
129010340Sstallion@opensolaris.org 	}
129110340Sstallion@opensolaris.org 
129210340Sstallion@opensolaris.org 	while (mp != NULL) {
129310340Sstallion@opensolaris.org 		if (!dnet_send(dnetp, mp)) {
129410340Sstallion@opensolaris.org 			mutex_exit(&dnetp->txlock);
129510340Sstallion@opensolaris.org 			return (mp);
129610340Sstallion@opensolaris.org 		}
129710340Sstallion@opensolaris.org 		mp = mp->b_next;
129810340Sstallion@opensolaris.org 	}
129910340Sstallion@opensolaris.org 
130010340Sstallion@opensolaris.org 	mutex_exit(&dnetp->txlock);
130110340Sstallion@opensolaris.org 
130210340Sstallion@opensolaris.org 	/*
130310340Sstallion@opensolaris.org 	 * Enable xmit interrupt in case we are running out of xmit descriptors
130410340Sstallion@opensolaris.org 	 * or there are more packets on the queue waiting to be transmitted.
130510340Sstallion@opensolaris.org 	 */
130610340Sstallion@opensolaris.org 	mutex_enter(&dnetp->intrlock);
130710340Sstallion@opensolaris.org 
130810340Sstallion@opensolaris.org 	enable_interrupts(dnetp);
130910340Sstallion@opensolaris.org 
131010340Sstallion@opensolaris.org 	/*
131110340Sstallion@opensolaris.org 	 * Kick the transmitter
131210340Sstallion@opensolaris.org 	 */
131310340Sstallion@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, TX_POLL_REG),
131410340Sstallion@opensolaris.org 	    TX_POLL_DEMAND);
131510340Sstallion@opensolaris.org 
131610340Sstallion@opensolaris.org 	mutex_exit(&dnetp->intrlock);
131710340Sstallion@opensolaris.org 
131810340Sstallion@opensolaris.org 	return (NULL);
131910340Sstallion@opensolaris.org }
132010340Sstallion@opensolaris.org 
132110340Sstallion@opensolaris.org static boolean_t
dnet_send(struct dnetinstance * dnetp,mblk_t * mp)132210340Sstallion@opensolaris.org dnet_send(struct dnetinstance *dnetp, mblk_t *mp)
132310340Sstallion@opensolaris.org {
13249860Sgdamore@opensolaris.org 	struct tx_desc_type	*ring = dnetp->tx_desc;
13259860Sgdamore@opensolaris.org 	int		mblen, totlen;
13269860Sgdamore@opensolaris.org 	int		index, end_index, start_index;
13279860Sgdamore@opensolaris.org 	int		avail;
13289860Sgdamore@opensolaris.org 	int		error;
13299860Sgdamore@opensolaris.org 	int		bufn;
13309860Sgdamore@opensolaris.org 	int		retval;
13319860Sgdamore@opensolaris.org 	mblk_t		*bp;
133210340Sstallion@opensolaris.org 
133310340Sstallion@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->txlock));
13349860Sgdamore@opensolaris.org 
13359860Sgdamore@opensolaris.org 	/* reclaim any xmit descriptors completed */
133610340Sstallion@opensolaris.org 	dnet_reclaim_Tx_desc(dnetp);
13379860Sgdamore@opensolaris.org 
13389860Sgdamore@opensolaris.org 	/*
13399860Sgdamore@opensolaris.org 	 * Use the data buffers from the message and construct the
13409860Sgdamore@opensolaris.org 	 * scatter/gather list by calling ddi_dma_addr_bind_handle().
13419860Sgdamore@opensolaris.org 	 */
134210340Sstallion@opensolaris.org 	error = 0;
13439860Sgdamore@opensolaris.org 	totlen = 0;
13449860Sgdamore@opensolaris.org 	bp = mp;
13459860Sgdamore@opensolaris.org 	bufn = 0;
13469860Sgdamore@opensolaris.org 	index = start_index = dnetp->tx_current_desc;
13479860Sgdamore@opensolaris.org 	avail = dnetp->free_desc;
13489860Sgdamore@opensolaris.org 	while (bp != NULL) {
13499860Sgdamore@opensolaris.org 		uint_t ncookies;
13509860Sgdamore@opensolaris.org 		ddi_dma_cookie_t dma_cookie;
13519860Sgdamore@opensolaris.org 
135210340Sstallion@opensolaris.org 		mblen = MBLKL(bp);
13539860Sgdamore@opensolaris.org 
13549860Sgdamore@opensolaris.org 		if (!mblen) {	/* skip zero-length message blocks */
13559860Sgdamore@opensolaris.org 			bp = bp->b_cont;
13569860Sgdamore@opensolaris.org 			continue;
13579860Sgdamore@opensolaris.org 		}
13589860Sgdamore@opensolaris.org 
13599860Sgdamore@opensolaris.org 		retval = ddi_dma_addr_bind_handle(dnetp->dma_handle_tx, NULL,
13609860Sgdamore@opensolaris.org 		    (caddr_t)bp->b_rptr, mblen,
13619860Sgdamore@opensolaris.org 		    DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
13629860Sgdamore@opensolaris.org 		    &dma_cookie, &ncookies);
13639860Sgdamore@opensolaris.org 
13649860Sgdamore@opensolaris.org 		switch (retval) {
13659860Sgdamore@opensolaris.org 		case DDI_DMA_MAPPED:
13669860Sgdamore@opensolaris.org 			break;		/* everything's fine */
13679860Sgdamore@opensolaris.org 
13689860Sgdamore@opensolaris.org 		case DDI_DMA_NORESOURCES:
13699860Sgdamore@opensolaris.org 			error = 1;	/* allow retry by gld */
13709860Sgdamore@opensolaris.org 			break;
13719860Sgdamore@opensolaris.org 
13729860Sgdamore@opensolaris.org 		case DDI_DMA_NOMAPPING:
13739860Sgdamore@opensolaris.org 		case DDI_DMA_INUSE:
13749860Sgdamore@opensolaris.org 		case DDI_DMA_TOOBIG:
13759860Sgdamore@opensolaris.org 		default:
13769860Sgdamore@opensolaris.org 			error = 2;	/* error, no retry */
13779860Sgdamore@opensolaris.org 			break;
13789860Sgdamore@opensolaris.org 		}
13799860Sgdamore@opensolaris.org 
13809860Sgdamore@opensolaris.org 		/*
13819860Sgdamore@opensolaris.org 		 * we can use two cookies per descriptor (i.e buffer1 and
13829860Sgdamore@opensolaris.org 		 * buffer2) so we need at least (ncookies+1)/2 descriptors.
13839860Sgdamore@opensolaris.org 		 */
13849860Sgdamore@opensolaris.org 		if (((ncookies + 1) >> 1) > dnetp->free_desc) {
13859860Sgdamore@opensolaris.org 			(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
13869860Sgdamore@opensolaris.org 			error = 1;
13879860Sgdamore@opensolaris.org 			break;
13889860Sgdamore@opensolaris.org 		}
13899860Sgdamore@opensolaris.org 
13909860Sgdamore@opensolaris.org 		/* setup the descriptors for this data buffer */
13919860Sgdamore@opensolaris.org 		while (ncookies) {
13929860Sgdamore@opensolaris.org 			end_index = index;
13939860Sgdamore@opensolaris.org 			if (bufn % 2) {
13949860Sgdamore@opensolaris.org 				ring[index].buffer2 =
13959860Sgdamore@opensolaris.org 				    (uint32_t)dma_cookie.dmac_address;
13969860Sgdamore@opensolaris.org 				ring[index].desc1.buffer_size2 =
13979860Sgdamore@opensolaris.org 				    dma_cookie.dmac_size;
13989860Sgdamore@opensolaris.org 				index = NextTXIndex(index); /* goto next desc */
13999860Sgdamore@opensolaris.org 			} else {
14009860Sgdamore@opensolaris.org 				/* initialize the descriptor */
14019860Sgdamore@opensolaris.org 				ASSERT(ring[index].desc0.own == 0);
14029860Sgdamore@opensolaris.org 				*(uint32_t *)&ring[index].desc0 = 0;
14039860Sgdamore@opensolaris.org 				*(uint32_t *)&ring[index].desc1 &=
14049860Sgdamore@opensolaris.org 				    DNET_END_OF_RING;
14059860Sgdamore@opensolaris.org 				ring[index].buffer1 =
14069860Sgdamore@opensolaris.org 				    (uint32_t)dma_cookie.dmac_address;
14079860Sgdamore@opensolaris.org 				ring[index].desc1.buffer_size1 =
14089860Sgdamore@opensolaris.org 				    dma_cookie.dmac_size;
14099860Sgdamore@opensolaris.org 				ring[index].buffer2 = (uint32_t)(0);
14109860Sgdamore@opensolaris.org 				dnetp->free_desc--;
14119860Sgdamore@opensolaris.org 				ASSERT(dnetp->free_desc >= 0);
14129860Sgdamore@opensolaris.org 			}
14139860Sgdamore@opensolaris.org 			totlen += dma_cookie.dmac_size;
14149860Sgdamore@opensolaris.org 			bufn++;
14159860Sgdamore@opensolaris.org 			if (--ncookies)
14169860Sgdamore@opensolaris.org 				ddi_dma_nextcookie(dnetp->dma_handle_tx,
14179860Sgdamore@opensolaris.org 				    &dma_cookie);
14189860Sgdamore@opensolaris.org 		}
14199860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
14209860Sgdamore@opensolaris.org 		bp = bp->b_cont;
14219860Sgdamore@opensolaris.org 	}
14229860Sgdamore@opensolaris.org 
14239860Sgdamore@opensolaris.org 	if (error == 1) {
14249860Sgdamore@opensolaris.org 		dnetp->stat_defer++;
14259860Sgdamore@opensolaris.org 		dnetp->free_desc = avail;
142610340Sstallion@opensolaris.org 		dnetp->need_tx_update = B_TRUE;
142710340Sstallion@opensolaris.org 		return (B_FALSE);
14289860Sgdamore@opensolaris.org 	} else if (error) {
14299860Sgdamore@opensolaris.org 		dnetp->free_desc = avail;
14309860Sgdamore@opensolaris.org 		freemsg(mp);
143110340Sstallion@opensolaris.org 		return (B_TRUE);	/* Drop packet, don't retry */
14329860Sgdamore@opensolaris.org 	}
14339860Sgdamore@opensolaris.org 
143410340Sstallion@opensolaris.org 	if (totlen > ETHERMAX + VLAN_TAGSZ) {
14359860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "DNET: tried to send large %d packet", totlen);
14369860Sgdamore@opensolaris.org 		dnetp->free_desc = avail;
14379860Sgdamore@opensolaris.org 		freemsg(mp);
143810340Sstallion@opensolaris.org 		return (B_TRUE);	/* Don't repeat this attempt */
14399860Sgdamore@opensolaris.org 	}
14409860Sgdamore@opensolaris.org 
14419860Sgdamore@opensolaris.org 	/*
14429860Sgdamore@opensolaris.org 	 * Remeber the message buffer pointer to do freemsg() at xmit
14439860Sgdamore@opensolaris.org 	 * interrupt time.
14449860Sgdamore@opensolaris.org 	 */
14459860Sgdamore@opensolaris.org 	dnetp->tx_msgbufp[end_index] = mp;
14469860Sgdamore@opensolaris.org 
14479860Sgdamore@opensolaris.org 	/*
14489860Sgdamore@opensolaris.org 	 * Now set the first/last buffer and own bits
14499860Sgdamore@opensolaris.org 	 * Since the 21040 looks for these bits set in the
14509860Sgdamore@opensolaris.org 	 * first buffer, work backwards in multiple buffers.
14519860Sgdamore@opensolaris.org 	 */
14529860Sgdamore@opensolaris.org 	ring[end_index].desc1.last_desc = 1;
14539860Sgdamore@opensolaris.org 	ring[end_index].desc1.int_on_comp = 1;
14549860Sgdamore@opensolaris.org 	for (index = end_index; index != start_index;
14559860Sgdamore@opensolaris.org 	    index = PrevTXIndex(index))
14569860Sgdamore@opensolaris.org 		ring[index].desc0.own = 1;
14579860Sgdamore@opensolaris.org 	ring[start_index].desc1.first_desc = 1;
14589860Sgdamore@opensolaris.org 	ring[start_index].desc0.own = 1;
14599860Sgdamore@opensolaris.org 
14609860Sgdamore@opensolaris.org 	dnetp->tx_current_desc = NextTXIndex(end_index);
14619860Sgdamore@opensolaris.org 
14629860Sgdamore@opensolaris.org 	/*
14639860Sgdamore@opensolaris.org 	 * Safety check: make sure end-of-ring is set in last desc.
14649860Sgdamore@opensolaris.org 	 */
14659860Sgdamore@opensolaris.org 	ASSERT(ring[dnetp->max_tx_desc-1].desc1.end_of_ring != 0);
14669860Sgdamore@opensolaris.org 
146710340Sstallion@opensolaris.org 	return (B_TRUE);
14689860Sgdamore@opensolaris.org }
14699860Sgdamore@opensolaris.org 
14709860Sgdamore@opensolaris.org /*
147110340Sstallion@opensolaris.org  *	dnet_intr() -- interrupt from board to inform us that a receive or
14729860Sgdamore@opensolaris.org  *	transmit has completed.
14739860Sgdamore@opensolaris.org  */
14749860Sgdamore@opensolaris.org static uint_t
dnet_intr(caddr_t arg)147510340Sstallion@opensolaris.org dnet_intr(caddr_t arg)
14769860Sgdamore@opensolaris.org {
147710340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = (struct dnetinstance *)arg;
147810340Sstallion@opensolaris.org 	uint32_t int_status;
14799860Sgdamore@opensolaris.org 
14809860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
148110340Sstallion@opensolaris.org 
14829860Sgdamore@opensolaris.org 	if (dnetp->suspended) {
14839860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
14849860Sgdamore@opensolaris.org 		return (DDI_INTR_UNCLAIMED);
14859860Sgdamore@opensolaris.org 	}
14869860Sgdamore@opensolaris.org 
14879860Sgdamore@opensolaris.org 	int_status = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg,
14889860Sgdamore@opensolaris.org 	    STATUS_REG));
14899860Sgdamore@opensolaris.org 
14909860Sgdamore@opensolaris.org 	/*
14919860Sgdamore@opensolaris.org 	 * If interrupt was not from this board
14929860Sgdamore@opensolaris.org 	 */
14939860Sgdamore@opensolaris.org 	if (!(int_status & (NORMAL_INTR_SUMM | ABNORMAL_INTR_SUMM))) {
14949860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
14959860Sgdamore@opensolaris.org 		return (DDI_INTR_UNCLAIMED);
14969860Sgdamore@opensolaris.org 	}
14979860Sgdamore@opensolaris.org 
14989860Sgdamore@opensolaris.org 	dnetp->stat_intr++;
14999860Sgdamore@opensolaris.org 
15009860Sgdamore@opensolaris.org 	if (int_status & GPTIMER_INTR) {
15019860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
15029860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, STATUS_REG), GPTIMER_INTR);
15039860Sgdamore@opensolaris.org 		if (dnetp->timer.cb)
15049860Sgdamore@opensolaris.org 			dnetp->timer.cb(dnetp);
15059860Sgdamore@opensolaris.org 		else
15069860Sgdamore@opensolaris.org 			cmn_err(CE_WARN, "dnet: unhandled timer interrupt");
15079860Sgdamore@opensolaris.org 	}
15089860Sgdamore@opensolaris.org 
15099860Sgdamore@opensolaris.org 	if (int_status & TX_INTR) {
15109860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
15119860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, STATUS_REG), TX_INTR);
15129860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->txlock);
151310340Sstallion@opensolaris.org 		if (dnetp->need_tx_update) {
15149860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->txlock);
15159860Sgdamore@opensolaris.org 			mutex_exit(&dnetp->intrlock);
151610340Sstallion@opensolaris.org 			mac_tx_update(dnetp->mac_handle);
15179860Sgdamore@opensolaris.org 			mutex_enter(&dnetp->intrlock);
15189860Sgdamore@opensolaris.org 			mutex_enter(&dnetp->txlock);
151910340Sstallion@opensolaris.org 			dnetp->need_tx_update = B_FALSE;
15209860Sgdamore@opensolaris.org 		}
15219860Sgdamore@opensolaris.org 		/* reclaim any xmit descriptors that are completed */
152210340Sstallion@opensolaris.org 		dnet_reclaim_Tx_desc(dnetp);
15239860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
15249860Sgdamore@opensolaris.org 	}
15259860Sgdamore@opensolaris.org 
15269860Sgdamore@opensolaris.org 	/*
15279860Sgdamore@opensolaris.org 	 * Check if receive interrupt bit is set
15289860Sgdamore@opensolaris.org 	 */
15299860Sgdamore@opensolaris.org 	if (int_status & (RX_INTR | RX_UNAVAIL_INTR)) {
15309860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
15319860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, STATUS_REG),
15329860Sgdamore@opensolaris.org 		    int_status & (RX_INTR | RX_UNAVAIL_INTR));
153310340Sstallion@opensolaris.org 		dnet_getp(dnetp);
15349860Sgdamore@opensolaris.org 	}
15359860Sgdamore@opensolaris.org 
15369860Sgdamore@opensolaris.org 	if (int_status & ABNORMAL_INTR_SUMM) {
15379860Sgdamore@opensolaris.org 		/*
15389860Sgdamore@opensolaris.org 		 * Check for system error
15399860Sgdamore@opensolaris.org 		 */
15409860Sgdamore@opensolaris.org 		if (int_status & SYS_ERR) {
15419860Sgdamore@opensolaris.org 			if ((int_status & SYS_ERR_BITS) == MASTER_ABORT)
15429860Sgdamore@opensolaris.org 				cmn_err(CE_WARN, "DNET: Bus Master Abort");
15439860Sgdamore@opensolaris.org 			if ((int_status & SYS_ERR_BITS) == TARGET_ABORT)
15449860Sgdamore@opensolaris.org 				cmn_err(CE_WARN, "DNET: Bus Target Abort");
15459860Sgdamore@opensolaris.org 			if ((int_status & SYS_ERR_BITS) == PARITY_ERROR)
15469860Sgdamore@opensolaris.org 				cmn_err(CE_WARN, "DNET: Parity error");
15479860Sgdamore@opensolaris.org 		}
15489860Sgdamore@opensolaris.org 
15499860Sgdamore@opensolaris.org 		/*
15509860Sgdamore@opensolaris.org 		 * If the jabber has timed out then reset the chip
15519860Sgdamore@opensolaris.org 		 */
15529860Sgdamore@opensolaris.org 		if (int_status & TX_JABBER_TIMEOUT)
15539860Sgdamore@opensolaris.org 			cmn_err(CE_WARN, "DNET: Jabber timeout.");
15549860Sgdamore@opensolaris.org 
15559860Sgdamore@opensolaris.org 		/*
15569860Sgdamore@opensolaris.org 		 * If an underflow has occurred, reset the chip
15579860Sgdamore@opensolaris.org 		 */
15589860Sgdamore@opensolaris.org 		if (int_status & TX_UNDERFLOW)
15599860Sgdamore@opensolaris.org 			cmn_err(CE_WARN, "DNET: Tx Underflow.");
15609860Sgdamore@opensolaris.org 
15619860Sgdamore@opensolaris.org #ifdef DNETDEBUG
15629860Sgdamore@opensolaris.org 		if (dnetdebug & DNETINT)
15639860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "Trying to reset...");
15649860Sgdamore@opensolaris.org #endif
156510340Sstallion@opensolaris.org 		dnet_reset_board(dnetp);
156610340Sstallion@opensolaris.org 		dnet_init_board(dnetp);
15679860Sgdamore@opensolaris.org 		/* XXX function return value ignored */
156810340Sstallion@opensolaris.org 		(void) dnet_start(dnetp);
15699860Sgdamore@opensolaris.org 	}
15709860Sgdamore@opensolaris.org 
15719860Sgdamore@opensolaris.org 	/*
157210340Sstallion@opensolaris.org 	 * Enable the interrupts. Enable xmit interrupt in case we are
15739860Sgdamore@opensolaris.org 	 * running out of free descriptors or if there are packets
15749860Sgdamore@opensolaris.org 	 * in the queue waiting to be transmitted.
15759860Sgdamore@opensolaris.org 	 */
157610340Sstallion@opensolaris.org 	enable_interrupts(dnetp);
15779860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
15789860Sgdamore@opensolaris.org 	return (DDI_INTR_CLAIMED);	/* Indicate it was our interrupt */
15799860Sgdamore@opensolaris.org }
15809860Sgdamore@opensolaris.org 
15819860Sgdamore@opensolaris.org static void
dnet_getp(struct dnetinstance * dnetp)158210340Sstallion@opensolaris.org dnet_getp(struct dnetinstance *dnetp)
15839860Sgdamore@opensolaris.org {
15849860Sgdamore@opensolaris.org 	int packet_length, index;
15859860Sgdamore@opensolaris.org 	mblk_t	*mp;
15869860Sgdamore@opensolaris.org 	caddr_t 	virtual_address;
15879860Sgdamore@opensolaris.org 	struct	rx_desc_type *desc = dnetp->rx_desc;
15889860Sgdamore@opensolaris.org 	int marker = dnetp->rx_current_desc;
15899860Sgdamore@opensolaris.org 	int misses;
15909860Sgdamore@opensolaris.org 
15919860Sgdamore@opensolaris.org 	if (!dnetp->overrun_workaround) {
15929860Sgdamore@opensolaris.org 		/*
15939860Sgdamore@opensolaris.org 		 * If the workaround is not in place, we must still update
15949860Sgdamore@opensolaris.org 		 * the missed frame statistic from the on-chip counter.
15959860Sgdamore@opensolaris.org 		 */
15969860Sgdamore@opensolaris.org 		misses = ddi_get32(dnetp->io_handle,
15979860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, MISSED_FRAME_REG));
15989860Sgdamore@opensolaris.org 		dnetp->stat_missed += (misses & MISSED_FRAME_MASK);
15999860Sgdamore@opensolaris.org 	}
16009860Sgdamore@opensolaris.org 
16019860Sgdamore@opensolaris.org 	/* While host owns the current descriptor */
16029860Sgdamore@opensolaris.org 	while (!(desc[dnetp->rx_current_desc].desc0.own)) {
16039860Sgdamore@opensolaris.org 		struct free_ptr *frp;
16049860Sgdamore@opensolaris.org 		caddr_t newbuf;
16059860Sgdamore@opensolaris.org 		struct rbuf_list *rp;
16069860Sgdamore@opensolaris.org 
16079860Sgdamore@opensolaris.org 		index = dnetp->rx_current_desc;
16089860Sgdamore@opensolaris.org 		ASSERT(desc[index].desc0.first_desc != 0);
16099860Sgdamore@opensolaris.org 
16109860Sgdamore@opensolaris.org 		/*
16119860Sgdamore@opensolaris.org 		 * DMA overrun errata from DEC: avoid possible bus hangs
16129860Sgdamore@opensolaris.org 		 * and data corruption
16139860Sgdamore@opensolaris.org 		 */
16149860Sgdamore@opensolaris.org 		if (dnetp->overrun_workaround &&
16159860Sgdamore@opensolaris.org 		    marker == dnetp->rx_current_desc) {
16169860Sgdamore@opensolaris.org 			int opn;
16179860Sgdamore@opensolaris.org 			do {
16189860Sgdamore@opensolaris.org 				marker = (marker+1) % dnetp->max_rx_desc;
16199860Sgdamore@opensolaris.org 			} while (!(dnetp->rx_desc[marker].desc0.own) &&
16209860Sgdamore@opensolaris.org 			    marker != index);
16219860Sgdamore@opensolaris.org 
16229860Sgdamore@opensolaris.org 			misses = ddi_get32(dnetp->io_handle,
16239860Sgdamore@opensolaris.org 			    REG32(dnetp->io_reg, MISSED_FRAME_REG));
16249860Sgdamore@opensolaris.org 			dnetp->stat_missed +=
16259860Sgdamore@opensolaris.org 			    (misses & MISSED_FRAME_MASK);
16269860Sgdamore@opensolaris.org 			if (misses & OVERFLOW_COUNTER_MASK) {
16279860Sgdamore@opensolaris.org 				/*
16289860Sgdamore@opensolaris.org 				 * Overflow(s) have occurred : stop receiver,
16299860Sgdamore@opensolaris.org 				 * and wait until in stopped state
16309860Sgdamore@opensolaris.org 				 */
16319860Sgdamore@opensolaris.org 				opn = ddi_get32(dnetp->io_handle,
16329860Sgdamore@opensolaris.org 				    REG32(dnetp->io_reg, OPN_MODE_REG));
16339860Sgdamore@opensolaris.org 				ddi_put32(dnetp->io_handle,
16349860Sgdamore@opensolaris.org 				    REG32(dnetp->io_reg, OPN_MODE_REG),
16359860Sgdamore@opensolaris.org 				    opn & ~(START_RECEIVE));
16369860Sgdamore@opensolaris.org 
16379860Sgdamore@opensolaris.org 				do {
16389860Sgdamore@opensolaris.org 					drv_usecwait(10);
16399860Sgdamore@opensolaris.org 				} while ((ddi_get32(dnetp->io_handle,
16409860Sgdamore@opensolaris.org 				    REG32(dnetp->io_reg, STATUS_REG)) &
16419860Sgdamore@opensolaris.org 				    RECEIVE_PROCESS_STATE) != 0);
16429860Sgdamore@opensolaris.org #ifdef DNETDEBUG
16439860Sgdamore@opensolaris.org 				if (dnetdebug & DNETRECV)
16449860Sgdamore@opensolaris.org 					cmn_err(CE_CONT, "^*");
16459860Sgdamore@opensolaris.org #endif
16469860Sgdamore@opensolaris.org 				/* Discard probably corrupt frames */
16479860Sgdamore@opensolaris.org 				while (!(dnetp->rx_desc[index].desc0.own)) {
16489860Sgdamore@opensolaris.org 					dnetp->rx_desc[index].desc0.own = 1;
16499860Sgdamore@opensolaris.org 					index = (index+1) % dnetp->max_rx_desc;
16509860Sgdamore@opensolaris.org 					dnetp->stat_missed++;
16519860Sgdamore@opensolaris.org 				}
16529860Sgdamore@opensolaris.org 
16539860Sgdamore@opensolaris.org 				/* restart the receiver */
16549860Sgdamore@opensolaris.org 				opn = ddi_get32(dnetp->io_handle,
16559860Sgdamore@opensolaris.org 				    REG32(dnetp->io_reg, OPN_MODE_REG));
16569860Sgdamore@opensolaris.org 				ddi_put32(dnetp->io_handle,
16579860Sgdamore@opensolaris.org 				    REG32(dnetp->io_reg, OPN_MODE_REG),
16589860Sgdamore@opensolaris.org 				    opn | START_RECEIVE);
16599860Sgdamore@opensolaris.org 				marker = dnetp->rx_current_desc = index;
16609860Sgdamore@opensolaris.org 				continue;
16619860Sgdamore@opensolaris.org 			}
16629860Sgdamore@opensolaris.org 			/*
16639860Sgdamore@opensolaris.org 			 * At this point, we know that all packets before
16649860Sgdamore@opensolaris.org 			 * "marker" were received before a dma overrun occurred
16659860Sgdamore@opensolaris.org 			 */
16669860Sgdamore@opensolaris.org 		}
16679860Sgdamore@opensolaris.org 
16689860Sgdamore@opensolaris.org 		/*
16699860Sgdamore@opensolaris.org 		 * If we get an oversized packet it could span multiple
16709860Sgdamore@opensolaris.org 		 * descriptors.  If this happens an error bit should be set.
16719860Sgdamore@opensolaris.org 		 */
16729860Sgdamore@opensolaris.org 		while (desc[index].desc0.last_desc == 0) {
16739860Sgdamore@opensolaris.org 			index = (index + 1) % dnetp->max_rx_desc;
16749860Sgdamore@opensolaris.org 			if (desc[index].desc0.own)
16759860Sgdamore@opensolaris.org 				return;	/* not done receiving large packet */
16769860Sgdamore@opensolaris.org 		}
16779860Sgdamore@opensolaris.org 		while (dnetp->rx_current_desc != index) {
16789860Sgdamore@opensolaris.org 			desc[dnetp->rx_current_desc].desc0.own = 1;
16799860Sgdamore@opensolaris.org 			dnetp->rx_current_desc =
16809860Sgdamore@opensolaris.org 			    (dnetp->rx_current_desc + 1) % dnetp->max_rx_desc;
16819860Sgdamore@opensolaris.org #ifdef DNETDEBUG
16829860Sgdamore@opensolaris.org 			if (dnetdebug & DNETRECV)
16839860Sgdamore@opensolaris.org 				cmn_err(CE_WARN, "dnet: received large packet");
16849860Sgdamore@opensolaris.org #endif
16859860Sgdamore@opensolaris.org 		}
16869860Sgdamore@opensolaris.org 
16879860Sgdamore@opensolaris.org 		packet_length = desc[index].desc0.frame_len;
16889860Sgdamore@opensolaris.org 
16899860Sgdamore@opensolaris.org 		/*
16909860Sgdamore@opensolaris.org 		 * Remove CRC from received data. This is an artefact of the
16919860Sgdamore@opensolaris.org 		 * 21x4x chip and should not be passed higher up the network
16929860Sgdamore@opensolaris.org 		 * stack.
16939860Sgdamore@opensolaris.org 		 */
16949860Sgdamore@opensolaris.org 		packet_length -= ETHERFCSL;
16959860Sgdamore@opensolaris.org 
16969860Sgdamore@opensolaris.org 		/* get the virtual address of the packet received */
16979860Sgdamore@opensolaris.org 		virtual_address =
16989860Sgdamore@opensolaris.org 		    dnetp->rx_buf_vaddr[index];
16999860Sgdamore@opensolaris.org 
17009860Sgdamore@opensolaris.org 		/*
17019860Sgdamore@opensolaris.org 		 * If no packet errors then do:
17029860Sgdamore@opensolaris.org 		 * 	1. Allocate a new receive buffer so that we can
17039860Sgdamore@opensolaris.org 		 *	   use the current buffer as streams buffer to
17049860Sgdamore@opensolaris.org 		 *	   avoid bcopy.
17059860Sgdamore@opensolaris.org 		 *	2. If we got a new receive buffer then allocate
17069860Sgdamore@opensolaris.org 		 *	   an mblk using desballoc().
17079860Sgdamore@opensolaris.org 		 *	3. Otherwise use the mblk from allocb() and do
17089860Sgdamore@opensolaris.org 		 *	   the bcopy.
17099860Sgdamore@opensolaris.org 		 */
17109860Sgdamore@opensolaris.org 		frp = NULL;
17119860Sgdamore@opensolaris.org 		rp = NULL;
17129860Sgdamore@opensolaris.org 		newbuf = NULL;
17139860Sgdamore@opensolaris.org 		mp = NULL;
171410340Sstallion@opensolaris.org 		if (!desc[index].desc0.err_summary ||
171510340Sstallion@opensolaris.org 		    (desc[index].desc0.frame2long &&
171610340Sstallion@opensolaris.org 		    packet_length < rx_buf_size)) {
17179860Sgdamore@opensolaris.org 			ASSERT(packet_length < rx_buf_size);
17189860Sgdamore@opensolaris.org 			/*
17199860Sgdamore@opensolaris.org 			 * Allocate another receive buffer for this descriptor.
17209860Sgdamore@opensolaris.org 			 * If we fail to allocate then we do the normal bcopy.
17219860Sgdamore@opensolaris.org 			 */
17229860Sgdamore@opensolaris.org 			rp = dnet_rbuf_alloc(dnetp->devinfo, 0);
17239860Sgdamore@opensolaris.org 			if (rp != NULL) {
17249860Sgdamore@opensolaris.org 				newbuf = rp->rbuf_vaddr;
17259860Sgdamore@opensolaris.org 				frp = kmem_zalloc(sizeof (*frp), KM_NOSLEEP);
17269860Sgdamore@opensolaris.org 				if (frp != NULL) {
17279860Sgdamore@opensolaris.org 					frp->free_rtn.free_func =
17289860Sgdamore@opensolaris.org 					    dnet_freemsg_buf;
17299860Sgdamore@opensolaris.org 					frp->free_rtn.free_arg = (char *)frp;
17309860Sgdamore@opensolaris.org 					frp->buf = virtual_address;
17319860Sgdamore@opensolaris.org 					mp = desballoc(
17329860Sgdamore@opensolaris.org 					    (uchar_t *)virtual_address,
17339860Sgdamore@opensolaris.org 					    packet_length, 0, &frp->free_rtn);
17349860Sgdamore@opensolaris.org 					if (mp == NULL) {
17359860Sgdamore@opensolaris.org 						kmem_free(frp, sizeof (*frp));
17369860Sgdamore@opensolaris.org 						dnet_rbuf_free((caddr_t)newbuf);
17379860Sgdamore@opensolaris.org 						frp = NULL;
17389860Sgdamore@opensolaris.org 						newbuf = NULL;
17399860Sgdamore@opensolaris.org 					}
17409860Sgdamore@opensolaris.org 				}
17419860Sgdamore@opensolaris.org 			}
17429860Sgdamore@opensolaris.org 			if (mp == NULL) {
17439860Sgdamore@opensolaris.org 				if (newbuf != NULL)
17449860Sgdamore@opensolaris.org 					dnet_rbuf_free((caddr_t)newbuf);
17459860Sgdamore@opensolaris.org 				mp = allocb(packet_length, 0);
17469860Sgdamore@opensolaris.org 			}
17479860Sgdamore@opensolaris.org 		}
17489860Sgdamore@opensolaris.org 
174910340Sstallion@opensolaris.org 		if ((desc[index].desc0.err_summary &&
175010340Sstallion@opensolaris.org 		    packet_length >= rx_buf_size) || mp == NULL) {
17519860Sgdamore@opensolaris.org 
17529860Sgdamore@opensolaris.org 			/* Update gld statistics */
17539860Sgdamore@opensolaris.org 			if (desc[index].desc0.err_summary)
175410340Sstallion@opensolaris.org 				update_rx_stats(dnetp, index);
17559860Sgdamore@opensolaris.org 			else
17569860Sgdamore@opensolaris.org 				dnetp->stat_norcvbuf++;
17579860Sgdamore@opensolaris.org 
17589860Sgdamore@opensolaris.org 			/*
17599860Sgdamore@opensolaris.org 			 * Reset ownership of the descriptor.
17609860Sgdamore@opensolaris.org 			 */
17619860Sgdamore@opensolaris.org 			desc[index].desc0.own = 1;
17629860Sgdamore@opensolaris.org 			dnetp->rx_current_desc =
17639860Sgdamore@opensolaris.org 			    (dnetp->rx_current_desc+1) % dnetp->max_rx_desc;
17649860Sgdamore@opensolaris.org 
17659860Sgdamore@opensolaris.org 			/* Demand receive polling by the chip */
17669860Sgdamore@opensolaris.org 			ddi_put32(dnetp->io_handle,
17679860Sgdamore@opensolaris.org 			    REG32(dnetp->io_reg, RX_POLL_REG), RX_POLL_DEMAND);
17689860Sgdamore@opensolaris.org 
17699860Sgdamore@opensolaris.org 			continue;
17709860Sgdamore@opensolaris.org 		}
17719860Sgdamore@opensolaris.org 
17729860Sgdamore@opensolaris.org 		if (newbuf != NULL) {
17739860Sgdamore@opensolaris.org 			uint32_t end_paddr;
17749860Sgdamore@opensolaris.org 			/* attach the new buffer to the rx descriptor */
17759860Sgdamore@opensolaris.org 			dnetp->rx_buf_vaddr[index] = newbuf;
17769860Sgdamore@opensolaris.org 			dnetp->rx_buf_paddr[index] = rp->rbuf_paddr;
17779860Sgdamore@opensolaris.org 			desc[index].buffer1 = rp->rbuf_paddr;
17789860Sgdamore@opensolaris.org 			desc[index].desc1.buffer_size1 = rx_buf_size;
17799860Sgdamore@opensolaris.org 			desc[index].desc1.buffer_size2 = 0;
17809860Sgdamore@opensolaris.org 			end_paddr = rp->rbuf_endpaddr;
17819860Sgdamore@opensolaris.org 			if ((desc[index].buffer1 & ~dnetp->pgmask) !=
17829860Sgdamore@opensolaris.org 			    (end_paddr & ~dnetp->pgmask)) {
17839860Sgdamore@opensolaris.org 				/* discontiguous */
17849860Sgdamore@opensolaris.org 				desc[index].buffer2 = end_paddr&~dnetp->pgmask;
17859860Sgdamore@opensolaris.org 				desc[index].desc1.buffer_size2 =
17869860Sgdamore@opensolaris.org 				    (end_paddr & dnetp->pgmask) + 1;
17879860Sgdamore@opensolaris.org 				desc[index].desc1.buffer_size1 =
17889860Sgdamore@opensolaris.org 				    rx_buf_size-desc[index].desc1.buffer_size2;
17899860Sgdamore@opensolaris.org 			}
17909860Sgdamore@opensolaris.org 		} else {
17919860Sgdamore@opensolaris.org 			/* couldn't allocate another buffer; copy the data */
17929860Sgdamore@opensolaris.org 			BCOPY((caddr_t)virtual_address, (caddr_t)mp->b_wptr,
17939860Sgdamore@opensolaris.org 			    packet_length);
17949860Sgdamore@opensolaris.org 		}
17959860Sgdamore@opensolaris.org 
17969860Sgdamore@opensolaris.org 		mp->b_wptr += packet_length;
17979860Sgdamore@opensolaris.org 
17989860Sgdamore@opensolaris.org 		desc[dnetp->rx_current_desc].desc0.own = 1;
17999860Sgdamore@opensolaris.org 
18009860Sgdamore@opensolaris.org 		/*
18019860Sgdamore@opensolaris.org 		 * Increment receive desc index. This is for the scan of
18029860Sgdamore@opensolaris.org 		 * next packet
18039860Sgdamore@opensolaris.org 		 */
18049860Sgdamore@opensolaris.org 		dnetp->rx_current_desc =
18059860Sgdamore@opensolaris.org 		    (dnetp->rx_current_desc+1) % dnetp->max_rx_desc;
18069860Sgdamore@opensolaris.org 
18079860Sgdamore@opensolaris.org 		/* Demand polling by chip */
18089860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
18099860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, RX_POLL_REG), RX_POLL_DEMAND);
18109860Sgdamore@opensolaris.org 
18119860Sgdamore@opensolaris.org 		/* send the packet upstream */
18129860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->intrlock);
181310340Sstallion@opensolaris.org 		mac_rx(dnetp->mac_handle, NULL, mp);
18149860Sgdamore@opensolaris.org 		mutex_enter(&dnetp->intrlock);
18159860Sgdamore@opensolaris.org 	}
18169860Sgdamore@opensolaris.org }
18179860Sgdamore@opensolaris.org /*
18189860Sgdamore@opensolaris.org  * Function to update receive statistics
18199860Sgdamore@opensolaris.org  */
18209860Sgdamore@opensolaris.org static void
update_rx_stats(struct dnetinstance * dnetp,int index)182110340Sstallion@opensolaris.org update_rx_stats(struct dnetinstance *dnetp, int index)
18229860Sgdamore@opensolaris.org {
18239860Sgdamore@opensolaris.org 	struct rx_desc_type *descp = &(dnetp->rx_desc[index]);
18249860Sgdamore@opensolaris.org 
18259860Sgdamore@opensolaris.org 	/*
18269860Sgdamore@opensolaris.org 	 * Update gld statistics
18279860Sgdamore@opensolaris.org 	 */
18289860Sgdamore@opensolaris.org 	dnetp->stat_errrcv++;
18299860Sgdamore@opensolaris.org 
18309860Sgdamore@opensolaris.org 	if (descp->desc0.overflow)	{
18319860Sgdamore@opensolaris.org 		/* FIFO Overrun */
18329860Sgdamore@opensolaris.org 		dnetp->stat_overflow++;
18339860Sgdamore@opensolaris.org 	}
18349860Sgdamore@opensolaris.org 
18359860Sgdamore@opensolaris.org 	if (descp->desc0.collision) {
18369860Sgdamore@opensolaris.org 		/*EMPTY*/
18379860Sgdamore@opensolaris.org 		/* Late Colllision on receive */
18389860Sgdamore@opensolaris.org 		/* no appropriate counter */
18399860Sgdamore@opensolaris.org 	}
18409860Sgdamore@opensolaris.org 
18419860Sgdamore@opensolaris.org 	if (descp->desc0.crc) {
18429860Sgdamore@opensolaris.org 		/* CRC Error */
18439860Sgdamore@opensolaris.org 		dnetp->stat_crc++;
18449860Sgdamore@opensolaris.org 	}
18459860Sgdamore@opensolaris.org 
18469860Sgdamore@opensolaris.org 	if (descp->desc0.runt_frame) {
18479860Sgdamore@opensolaris.org 		/* Runt Error */
18489860Sgdamore@opensolaris.org 		dnetp->stat_short++;
18499860Sgdamore@opensolaris.org 	}
18509860Sgdamore@opensolaris.org 
18519860Sgdamore@opensolaris.org 	if (descp->desc0.desc_err) {
18529860Sgdamore@opensolaris.org 		/*EMPTY*/
18539860Sgdamore@opensolaris.org 		/* Not enough receive descriptors */
185410340Sstallion@opensolaris.org 		/* This condition is accounted in dnet_intr() */
18559860Sgdamore@opensolaris.org 	}
18569860Sgdamore@opensolaris.org 
18579860Sgdamore@opensolaris.org 	if (descp->desc0.frame2long) {
18589860Sgdamore@opensolaris.org 		dnetp->stat_frame++;
18599860Sgdamore@opensolaris.org 	}
18609860Sgdamore@opensolaris.org }
18619860Sgdamore@opensolaris.org 
18629860Sgdamore@opensolaris.org /*
18639860Sgdamore@opensolaris.org  * Function to update transmit statistics
18649860Sgdamore@opensolaris.org  */
18659860Sgdamore@opensolaris.org static void
update_tx_stats(struct dnetinstance * dnetp,int index)186610340Sstallion@opensolaris.org update_tx_stats(struct dnetinstance *dnetp, int index)
18679860Sgdamore@opensolaris.org {
18689860Sgdamore@opensolaris.org 	struct tx_desc_type *descp = &(dnetp->tx_desc[index]);
18699860Sgdamore@opensolaris.org 	int	fd;
18709860Sgdamore@opensolaris.org 	media_block_t	*block = dnetp->selected_media_block;
18719860Sgdamore@opensolaris.org 
18729860Sgdamore@opensolaris.org 
18739860Sgdamore@opensolaris.org 	/* Update gld statistics */
18749860Sgdamore@opensolaris.org 	dnetp->stat_errxmt++;
18759860Sgdamore@opensolaris.org 
18769860Sgdamore@opensolaris.org 	/* If we're in full-duplex don't count collisions or carrier loss. */
18779860Sgdamore@opensolaris.org 	if (dnetp->mii_up) {
18789860Sgdamore@opensolaris.org 		fd = dnetp->mii_duplex;
18799860Sgdamore@opensolaris.org 	} else {
18809860Sgdamore@opensolaris.org 		/* Rely on media code */
18819860Sgdamore@opensolaris.org 		fd = block->media_code == MEDIA_TP_FD ||
18829860Sgdamore@opensolaris.org 		    block->media_code == MEDIA_SYM_SCR_FD;
18839860Sgdamore@opensolaris.org 	}
18849860Sgdamore@opensolaris.org 
18859860Sgdamore@opensolaris.org 	if (descp->desc0.collision_count && !fd) {
18869860Sgdamore@opensolaris.org 		dnetp->stat_collisions += descp->desc0.collision_count;
18879860Sgdamore@opensolaris.org 	}
18889860Sgdamore@opensolaris.org 
18899860Sgdamore@opensolaris.org 	if (descp->desc0.late_collision && !fd) {
18909860Sgdamore@opensolaris.org 		dnetp->stat_xmtlatecoll++;
18919860Sgdamore@opensolaris.org 	}
18929860Sgdamore@opensolaris.org 
18939860Sgdamore@opensolaris.org 	if (descp->desc0.excess_collision && !fd) {
18949860Sgdamore@opensolaris.org 		dnetp->stat_excoll++;
18959860Sgdamore@opensolaris.org 	}
18969860Sgdamore@opensolaris.org 
18979860Sgdamore@opensolaris.org 	if (descp->desc0.underflow) {
18989860Sgdamore@opensolaris.org 		dnetp->stat_underflow++;
18999860Sgdamore@opensolaris.org 	}
19009860Sgdamore@opensolaris.org 
19019860Sgdamore@opensolaris.org #if 0
19029860Sgdamore@opensolaris.org 	if (descp->desc0.tx_jabber_to) {
19039860Sgdamore@opensolaris.org 		/* no appropriate counter */
19049860Sgdamore@opensolaris.org 	}
19059860Sgdamore@opensolaris.org #endif
19069860Sgdamore@opensolaris.org 
19079860Sgdamore@opensolaris.org 	if (descp->desc0.carrier_loss && !fd) {
19089860Sgdamore@opensolaris.org 		dnetp->stat_nocarrier++;
19099860Sgdamore@opensolaris.org 	}
19109860Sgdamore@opensolaris.org 
19119860Sgdamore@opensolaris.org 	if (descp->desc0.no_carrier && !fd) {
19129860Sgdamore@opensolaris.org 		dnetp->stat_nocarrier++;
19139860Sgdamore@opensolaris.org 	}
19149860Sgdamore@opensolaris.org }
19159860Sgdamore@opensolaris.org 
19169860Sgdamore@opensolaris.org /*
19179860Sgdamore@opensolaris.org  *	========== Media Selection Setup Routines ==========
19189860Sgdamore@opensolaris.org  */
19199860Sgdamore@opensolaris.org 
19209860Sgdamore@opensolaris.org 
19219860Sgdamore@opensolaris.org static void
write_gpr(struct dnetinstance * dnetp,uint32_t val)19229860Sgdamore@opensolaris.org write_gpr(struct dnetinstance *dnetp, uint32_t val)
19239860Sgdamore@opensolaris.org {
19249860Sgdamore@opensolaris.org #ifdef DEBUG
19259860Sgdamore@opensolaris.org 	if (dnetdebug & DNETREGCFG)
19269860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "GPR: %x", val);
19279860Sgdamore@opensolaris.org #endif
19289860Sgdamore@opensolaris.org 	switch (dnetp->board_type) {
19299860Sgdamore@opensolaris.org 	case DEVICE_ID_21143:
19309860Sgdamore@opensolaris.org 		/* Set the correct bit for a control write */
19319860Sgdamore@opensolaris.org 		if (val & GPR_CONTROL_WRITE)
19329860Sgdamore@opensolaris.org 			val |= CWE_21143, val &= ~GPR_CONTROL_WRITE;
19339860Sgdamore@opensolaris.org 		/* Write to upper half of CSR15 */
19349860Sgdamore@opensolaris.org 		dnetp->gprsia = (dnetp->gprsia & 0xffff) | (val << 16);
19359860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
19369860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_GENERAL_REG), dnetp->gprsia);
19379860Sgdamore@opensolaris.org 		break;
19389860Sgdamore@opensolaris.org 	default:
19399860Sgdamore@opensolaris.org 		/* Set the correct bit for a control write */
19409860Sgdamore@opensolaris.org 		if (val & GPR_CONTROL_WRITE)
19419860Sgdamore@opensolaris.org 			val |= CWE_21140, val &= ~GPR_CONTROL_WRITE;
19429860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, GP_REG), val);
19439860Sgdamore@opensolaris.org 		break;
19449860Sgdamore@opensolaris.org 	}
19459860Sgdamore@opensolaris.org }
19469860Sgdamore@opensolaris.org 
19479860Sgdamore@opensolaris.org static uint32_t
read_gpr(struct dnetinstance * dnetp)19489860Sgdamore@opensolaris.org read_gpr(struct dnetinstance *dnetp)
19499860Sgdamore@opensolaris.org {
19509860Sgdamore@opensolaris.org 	switch (dnetp->board_type) {
19519860Sgdamore@opensolaris.org 	case DEVICE_ID_21143:
19529860Sgdamore@opensolaris.org 		/* Read upper half of CSR15 */
19539860Sgdamore@opensolaris.org 		return (ddi_get32(dnetp->io_handle,
19549860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_GENERAL_REG)) >> 16);
19559860Sgdamore@opensolaris.org 	default:
19569860Sgdamore@opensolaris.org 		return (ddi_get32(dnetp->io_handle,
19579860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, GP_REG)));
19589860Sgdamore@opensolaris.org 	}
19599860Sgdamore@opensolaris.org }
19609860Sgdamore@opensolaris.org 
19619860Sgdamore@opensolaris.org static void
set_gpr(struct dnetinstance * dnetp)196210340Sstallion@opensolaris.org set_gpr(struct dnetinstance *dnetp)
19639860Sgdamore@opensolaris.org {
19649860Sgdamore@opensolaris.org 	uint32_t *sequence;
19659860Sgdamore@opensolaris.org 	int len;
19669860Sgdamore@opensolaris.org 	LEAF_FORMAT *leaf = &dnetp->sr.leaf[dnetp->leaf];
19679860Sgdamore@opensolaris.org 	media_block_t *block = dnetp->selected_media_block;
19689860Sgdamore@opensolaris.org 	int i;
19699860Sgdamore@opensolaris.org 
19709860Sgdamore@opensolaris.org 	if (ddi_getlongprop(DDI_DEV_T_ANY, dnetp->devinfo,
19719860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, "gpr-sequence", (caddr_t)&sequence,
19729860Sgdamore@opensolaris.org 	    &len) == DDI_PROP_SUCCESS) {
19739860Sgdamore@opensolaris.org 		for (i = 0; i < len / sizeof (uint32_t); i++)
19749860Sgdamore@opensolaris.org 			write_gpr(dnetp, sequence[i]);
19759860Sgdamore@opensolaris.org 		kmem_free(sequence, len);
19769860Sgdamore@opensolaris.org 	} else {
19779860Sgdamore@opensolaris.org 		/*
19789860Sgdamore@opensolaris.org 		 * Write the reset sequence if this is the first time this
19799860Sgdamore@opensolaris.org 		 * block has been selected.
19809860Sgdamore@opensolaris.org 		 */
19819860Sgdamore@opensolaris.org 		if (block->rstseqlen) {
19829860Sgdamore@opensolaris.org 			for (i = 0; i < block->rstseqlen; i++)
19839860Sgdamore@opensolaris.org 				write_gpr(dnetp, block->rstseq[i]);
19849860Sgdamore@opensolaris.org 			/*
19859860Sgdamore@opensolaris.org 			 * XXX Legacy blocks do not have reset sequences, so the
19869860Sgdamore@opensolaris.org 			 * static blocks will never be modified by this
19879860Sgdamore@opensolaris.org 			 */
19889860Sgdamore@opensolaris.org 			block->rstseqlen = 0;
19899860Sgdamore@opensolaris.org 		}
19909860Sgdamore@opensolaris.org 		if (leaf->gpr)
19919860Sgdamore@opensolaris.org 			write_gpr(dnetp, leaf->gpr | GPR_CONTROL_WRITE);
19929860Sgdamore@opensolaris.org 
19939860Sgdamore@opensolaris.org 		/* write GPR sequence each time */
19949860Sgdamore@opensolaris.org 		for (i = 0; i < block->gprseqlen; i++)
19959860Sgdamore@opensolaris.org 			write_gpr(dnetp, block->gprseq[i]);
19969860Sgdamore@opensolaris.org 	}
19979860Sgdamore@opensolaris.org 
19989860Sgdamore@opensolaris.org 	/* This has possibly caused a PHY to reset.  Let MII know */
19999860Sgdamore@opensolaris.org 	if (dnetp->phyaddr != -1)
20009860Sgdamore@opensolaris.org 		/* XXX function return value ignored */
20019860Sgdamore@opensolaris.org 		(void) mii_sync(dnetp->mii, dnetp->phyaddr);
20029860Sgdamore@opensolaris.org 	drv_usecwait(5);
20039860Sgdamore@opensolaris.org }
20049860Sgdamore@opensolaris.org 
20059860Sgdamore@opensolaris.org /* set_opr() - must be called with intrlock held */
20069860Sgdamore@opensolaris.org 
20079860Sgdamore@opensolaris.org static void
set_opr(struct dnetinstance * dnetp)200810340Sstallion@opensolaris.org set_opr(struct dnetinstance *dnetp)
20099860Sgdamore@opensolaris.org {
20109860Sgdamore@opensolaris.org 	uint32_t fd, mb1, sf;
20119860Sgdamore@opensolaris.org 
20129860Sgdamore@opensolaris.org 	int 		opnmode_len;
20139860Sgdamore@opensolaris.org 	uint32_t val;
20149860Sgdamore@opensolaris.org 	media_block_t *block = dnetp->selected_media_block;
20159860Sgdamore@opensolaris.org 
20169860Sgdamore@opensolaris.org 	ASSERT(block);
20179860Sgdamore@opensolaris.org 
20189860Sgdamore@opensolaris.org 	/* Check for custom "opnmode_reg" property */
20199860Sgdamore@opensolaris.org 	opnmode_len = sizeof (val);
20209860Sgdamore@opensolaris.org 	if (ddi_prop_op(DDI_DEV_T_ANY, dnetp->devinfo,
20219860Sgdamore@opensolaris.org 	    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "opnmode_reg",
20229860Sgdamore@opensolaris.org 	    (caddr_t)&val, &opnmode_len) != DDI_PROP_SUCCESS)
20239860Sgdamore@opensolaris.org 		opnmode_len = 0;
20249860Sgdamore@opensolaris.org 
20259860Sgdamore@opensolaris.org 	/* Some bits exist only on 21140 and greater */
20269860Sgdamore@opensolaris.org 	if (dnetp->board_type != DEVICE_ID_21040 &&
20279860Sgdamore@opensolaris.org 	    dnetp->board_type != DEVICE_ID_21041) {
20289860Sgdamore@opensolaris.org 		mb1 = OPN_REG_MB1;
20299860Sgdamore@opensolaris.org 		sf = STORE_AND_FORWARD;
20309860Sgdamore@opensolaris.org 	} else {
20319860Sgdamore@opensolaris.org 		mb1 = sf = 0;
20329860Sgdamore@opensolaris.org 		mb1 = OPN_REG_MB1; /* Needed for 21040? */
20339860Sgdamore@opensolaris.org 	}
20349860Sgdamore@opensolaris.org 
20359860Sgdamore@opensolaris.org 	if (opnmode_len) {
20369860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
20379860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, OPN_MODE_REG), val);
203810340Sstallion@opensolaris.org 		dnet_reset_board(dnetp);
20399860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
20409860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, OPN_MODE_REG), val);
20419860Sgdamore@opensolaris.org 		return;
20429860Sgdamore@opensolaris.org 	}
20439860Sgdamore@opensolaris.org 
20449860Sgdamore@opensolaris.org 	/*
20459860Sgdamore@opensolaris.org 	 * Set each bit in CSR6 that we want
20469860Sgdamore@opensolaris.org 	 */
20479860Sgdamore@opensolaris.org 
20489860Sgdamore@opensolaris.org 	/* Always want these bits set */
20499860Sgdamore@opensolaris.org 	val = HASH_FILTERING | HASH_ONLY | TX_THRESHOLD_160 | mb1 | sf;
20509860Sgdamore@opensolaris.org 
20519860Sgdamore@opensolaris.org 	/* Promiscuous mode */
20529860Sgdamore@opensolaris.org 	val |= dnetp->promisc ? PROM_MODE : 0;
20539860Sgdamore@opensolaris.org 
20549860Sgdamore@opensolaris.org 	/* Scrambler for SYM style media */
20559860Sgdamore@opensolaris.org 	val |= ((block->command & CMD_SCR) && !dnetp->disable_scrambler) ?
20569860Sgdamore@opensolaris.org 	    SCRAMBLER_MODE : 0;
20579860Sgdamore@opensolaris.org 
20589860Sgdamore@opensolaris.org 	/* Full duplex */
20599860Sgdamore@opensolaris.org 	if (dnetp->mii_up) {
20609860Sgdamore@opensolaris.org 		fd = dnetp->mii_duplex;
20619860Sgdamore@opensolaris.org 	} else {
20629860Sgdamore@opensolaris.org 		/* Rely on media code */
20639860Sgdamore@opensolaris.org 		fd = block->media_code == MEDIA_TP_FD ||
20649860Sgdamore@opensolaris.org 		    block->media_code == MEDIA_SYM_SCR_FD;
20659860Sgdamore@opensolaris.org 	}
20669860Sgdamore@opensolaris.org 
20679860Sgdamore@opensolaris.org 	/* Port select (and therefore, heartbeat disable) */
20689860Sgdamore@opensolaris.org 	val |= block->command & CMD_PS ? (PORT_SELECT | HEARTBEAT_DISABLE) : 0;
20699860Sgdamore@opensolaris.org 
20709860Sgdamore@opensolaris.org 	/* PCS function */
20719860Sgdamore@opensolaris.org 	val |= (block->command) & CMD_PCS ? PCS_FUNCTION : 0;
20729860Sgdamore@opensolaris.org 	val |= fd ? FULL_DUPLEX : 0;
20739860Sgdamore@opensolaris.org 
20749860Sgdamore@opensolaris.org #ifdef DNETDEBUG
20759860Sgdamore@opensolaris.org 	if (dnetdebug & DNETREGCFG)
20769860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "OPN: %x", val);
20779860Sgdamore@opensolaris.org #endif
20789860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG), val);
207910340Sstallion@opensolaris.org 	dnet_reset_board(dnetp);
20809860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG), val);
20819860Sgdamore@opensolaris.org }
20829860Sgdamore@opensolaris.org 
20839860Sgdamore@opensolaris.org static void
set_sia(struct dnetinstance * dnetp)208410340Sstallion@opensolaris.org set_sia(struct dnetinstance *dnetp)
20859860Sgdamore@opensolaris.org {
20869860Sgdamore@opensolaris.org 	media_block_t *block = dnetp->selected_media_block;
20879860Sgdamore@opensolaris.org 
20889860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
20899860Sgdamore@opensolaris.org 	if (block->type == 2) {
20909860Sgdamore@opensolaris.org 		int sia_delay;
20919860Sgdamore@opensolaris.org #ifdef DNETDEBUG
20929860Sgdamore@opensolaris.org 		if (dnetdebug & DNETREGCFG)
20939860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE,
20949860Sgdamore@opensolaris.org 			    "SIA: CSR13: %x, CSR14: %x, CSR15: %x",
20959860Sgdamore@opensolaris.org 			    block->un.sia.csr13,
20969860Sgdamore@opensolaris.org 			    block->un.sia.csr14,
20979860Sgdamore@opensolaris.org 			    block->un.sia.csr15);
20989860Sgdamore@opensolaris.org #endif
20999860Sgdamore@opensolaris.org 		sia_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
21009860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, "sia-delay", 10000);
21019860Sgdamore@opensolaris.org 
21029860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
21039860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_CONNECT_REG), 0);
21049860Sgdamore@opensolaris.org 
21059860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, SIA_TXRX_REG),
21069860Sgdamore@opensolaris.org 		    block->un.sia.csr14);
21079860Sgdamore@opensolaris.org 
21089860Sgdamore@opensolaris.org 		/*
21099860Sgdamore@opensolaris.org 		 * For '143, we need to write through a copy of the register
21109860Sgdamore@opensolaris.org 		 * to keep the GP half intact
21119860Sgdamore@opensolaris.org 		 */
21129860Sgdamore@opensolaris.org 		dnetp->gprsia = (dnetp->gprsia&0xffff0000)|block->un.sia.csr15;
21139860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
21149860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_GENERAL_REG),
21159860Sgdamore@opensolaris.org 		    dnetp->gprsia);
21169860Sgdamore@opensolaris.org 
21179860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
21189860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_CONNECT_REG),
21199860Sgdamore@opensolaris.org 		    block->un.sia.csr13);
21209860Sgdamore@opensolaris.org 
21219860Sgdamore@opensolaris.org 		drv_usecwait(sia_delay);
21229860Sgdamore@opensolaris.org 
21239860Sgdamore@opensolaris.org 	} else if (dnetp->board_type != DEVICE_ID_21140) {
21249860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
21259860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_CONNECT_REG), 0);
21269860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
21279860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, SIA_TXRX_REG), 0);
21289860Sgdamore@opensolaris.org 	}
21299860Sgdamore@opensolaris.org }
21309860Sgdamore@opensolaris.org 
21319860Sgdamore@opensolaris.org /*
21329860Sgdamore@opensolaris.org  * This function (re)allocates the receive and transmit buffers and
21339860Sgdamore@opensolaris.org  * descriptors.  It can be called more than once per instance, though
21349860Sgdamore@opensolaris.org  * currently it is only called from attach.  It should only be called
21359860Sgdamore@opensolaris.org  * while the device is reset.
21369860Sgdamore@opensolaris.org  */
21379860Sgdamore@opensolaris.org static int
dnet_alloc_bufs(struct dnetinstance * dnetp)213810340Sstallion@opensolaris.org dnet_alloc_bufs(struct dnetinstance *dnetp)
21399860Sgdamore@opensolaris.org {
21409860Sgdamore@opensolaris.org 	int i;
21419860Sgdamore@opensolaris.org 	size_t len;
21429860Sgdamore@opensolaris.org 	int page_size;
21439860Sgdamore@opensolaris.org 	int realloc = 0;
21449860Sgdamore@opensolaris.org 	int nrecv_desc_old = 0;
21459860Sgdamore@opensolaris.org 	ddi_dma_cookie_t cookie;
21469860Sgdamore@opensolaris.org 	uint_t ncookies;
21479860Sgdamore@opensolaris.org 
21489860Sgdamore@opensolaris.org 	/*
21499860Sgdamore@opensolaris.org 	 * check if we are trying to reallocate with different xmit/recv
21509860Sgdamore@opensolaris.org 	 * descriptor ring sizes.
21519860Sgdamore@opensolaris.org 	 */
21529860Sgdamore@opensolaris.org 	if ((dnetp->tx_desc != NULL) &&
21539860Sgdamore@opensolaris.org 	    (dnetp->nxmit_desc != dnetp->max_tx_desc))
21549860Sgdamore@opensolaris.org 		realloc = 1;
21559860Sgdamore@opensolaris.org 
21569860Sgdamore@opensolaris.org 	if ((dnetp->rx_desc != NULL) &&
21579860Sgdamore@opensolaris.org 	    (dnetp->nrecv_desc != dnetp->max_rx_desc))
21589860Sgdamore@opensolaris.org 		realloc = 1;
21599860Sgdamore@opensolaris.org 
21609860Sgdamore@opensolaris.org 	/* free up the old buffers if we are reallocating them */
21619860Sgdamore@opensolaris.org 	if (realloc) {
21629860Sgdamore@opensolaris.org 		nrecv_desc_old = dnetp->nrecv_desc;
216310340Sstallion@opensolaris.org 		dnet_free_bufs(dnetp); /* free the old buffers */
21649860Sgdamore@opensolaris.org 	}
21659860Sgdamore@opensolaris.org 
21669860Sgdamore@opensolaris.org 	if (dnetp->dma_handle == NULL)
21679860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
21689860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, 0, &dnetp->dma_handle) != DDI_SUCCESS)
21699860Sgdamore@opensolaris.org 			return (FAILURE);
21709860Sgdamore@opensolaris.org 
21719860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_tx == NULL)
21729860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr_tx,
21739860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, 0, &dnetp->dma_handle_tx) != DDI_SUCCESS)
21749860Sgdamore@opensolaris.org 			return (FAILURE);
21759860Sgdamore@opensolaris.org 
21769860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_txdesc == NULL)
21779860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
21789860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, 0, &dnetp->dma_handle_txdesc) != DDI_SUCCESS)
21799860Sgdamore@opensolaris.org 			return (FAILURE);
21809860Sgdamore@opensolaris.org 
21819860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_setbuf == NULL)
21829860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
21839860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, 0, &dnetp->dma_handle_setbuf) != DDI_SUCCESS)
21849860Sgdamore@opensolaris.org 			return (FAILURE);
21859860Sgdamore@opensolaris.org 
21869860Sgdamore@opensolaris.org 	page_size = ddi_ptob(dnetp->devinfo, 1);
21879860Sgdamore@opensolaris.org 
21889860Sgdamore@opensolaris.org 	dnetp->pgmask = page_size - 1;
21899860Sgdamore@opensolaris.org 
21909860Sgdamore@opensolaris.org 	/* allocate setup buffer if necessary */
21919860Sgdamore@opensolaris.org 	if (dnetp->setup_buf_vaddr == NULL) {
21929860Sgdamore@opensolaris.org 		if (ddi_dma_mem_alloc(dnetp->dma_handle_setbuf,
21939860Sgdamore@opensolaris.org 		    SETUPBUF_SIZE, &accattr, DDI_DMA_STREAMING,
21949860Sgdamore@opensolaris.org 		    DDI_DMA_DONTWAIT, 0, (caddr_t *)&dnetp->setup_buf_vaddr,
21959860Sgdamore@opensolaris.org 		    &len, &dnetp->setup_buf_acchdl) != DDI_SUCCESS)
21969860Sgdamore@opensolaris.org 			return (FAILURE);
21979860Sgdamore@opensolaris.org 
21989860Sgdamore@opensolaris.org 		if (ddi_dma_addr_bind_handle(dnetp->dma_handle_setbuf,
21999860Sgdamore@opensolaris.org 		    NULL, dnetp->setup_buf_vaddr, SETUPBUF_SIZE,
22009860Sgdamore@opensolaris.org 		    DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
22019860Sgdamore@opensolaris.org 		    NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
22029860Sgdamore@opensolaris.org 			return (FAILURE);
22039860Sgdamore@opensolaris.org 
22049860Sgdamore@opensolaris.org 		dnetp->setup_buf_paddr = cookie.dmac_address;
22059860Sgdamore@opensolaris.org 		bzero(dnetp->setup_buf_vaddr, len);
22069860Sgdamore@opensolaris.org 	}
22079860Sgdamore@opensolaris.org 
22089860Sgdamore@opensolaris.org 	/* allocate xmit descriptor array of size dnetp->max_tx_desc */
22099860Sgdamore@opensolaris.org 	if (dnetp->tx_desc == NULL) {
22109860Sgdamore@opensolaris.org 		if (ddi_dma_mem_alloc(dnetp->dma_handle_txdesc,
22119860Sgdamore@opensolaris.org 		    sizeof (struct tx_desc_type) * dnetp->max_tx_desc,
22129860Sgdamore@opensolaris.org 		    &accattr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
22139860Sgdamore@opensolaris.org 		    (caddr_t *)&dnetp->tx_desc, &len,
22149860Sgdamore@opensolaris.org 		    &dnetp->tx_desc_acchdl) != DDI_SUCCESS)
22159860Sgdamore@opensolaris.org 			return (FAILURE);
22169860Sgdamore@opensolaris.org 
22179860Sgdamore@opensolaris.org 		if (ddi_dma_addr_bind_handle(dnetp->dma_handle_txdesc,
22189860Sgdamore@opensolaris.org 		    NULL, (caddr_t)dnetp->tx_desc,
22199860Sgdamore@opensolaris.org 		    sizeof (struct tx_desc_type) * dnetp->max_tx_desc,
22209860Sgdamore@opensolaris.org 		    DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
22219860Sgdamore@opensolaris.org 		    NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
22229860Sgdamore@opensolaris.org 			return (FAILURE);
22239860Sgdamore@opensolaris.org 		dnetp->tx_desc_paddr = cookie.dmac_address;
22249860Sgdamore@opensolaris.org 		bzero(dnetp->tx_desc, len);
22259860Sgdamore@opensolaris.org 		dnetp->nxmit_desc = dnetp->max_tx_desc;
22269860Sgdamore@opensolaris.org 
22279860Sgdamore@opensolaris.org 		dnetp->tx_msgbufp =
22289860Sgdamore@opensolaris.org 		    kmem_zalloc(dnetp->max_tx_desc * sizeof (mblk_t **),
22299860Sgdamore@opensolaris.org 		    KM_SLEEP);
22309860Sgdamore@opensolaris.org 	}
22319860Sgdamore@opensolaris.org 
22329860Sgdamore@opensolaris.org 	/* allocate receive descriptor array of size dnetp->max_rx_desc */
22339860Sgdamore@opensolaris.org 	if (dnetp->rx_desc == NULL) {
22349860Sgdamore@opensolaris.org 		int ndesc;
22359860Sgdamore@opensolaris.org 
22369860Sgdamore@opensolaris.org 		if (ddi_dma_mem_alloc(dnetp->dma_handle,
22379860Sgdamore@opensolaris.org 		    sizeof (struct rx_desc_type) * dnetp->max_rx_desc,
22389860Sgdamore@opensolaris.org 		    &accattr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
22399860Sgdamore@opensolaris.org 		    (caddr_t *)&dnetp->rx_desc, &len,
22409860Sgdamore@opensolaris.org 		    &dnetp->rx_desc_acchdl) != DDI_SUCCESS)
22419860Sgdamore@opensolaris.org 			return (FAILURE);
22429860Sgdamore@opensolaris.org 
22439860Sgdamore@opensolaris.org 		if (ddi_dma_addr_bind_handle(dnetp->dma_handle,
22449860Sgdamore@opensolaris.org 		    NULL, (caddr_t)dnetp->rx_desc,
22459860Sgdamore@opensolaris.org 		    sizeof (struct rx_desc_type) * dnetp->max_rx_desc,
22469860Sgdamore@opensolaris.org 		    DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
22479860Sgdamore@opensolaris.org 		    NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
22489860Sgdamore@opensolaris.org 			return (FAILURE);
22499860Sgdamore@opensolaris.org 
22509860Sgdamore@opensolaris.org 		dnetp->rx_desc_paddr = cookie.dmac_address;
22519860Sgdamore@opensolaris.org 		bzero(dnetp->rx_desc, len);
22529860Sgdamore@opensolaris.org 		dnetp->nrecv_desc = dnetp->max_rx_desc;
22539860Sgdamore@opensolaris.org 
22549860Sgdamore@opensolaris.org 		dnetp->rx_buf_vaddr =
22559860Sgdamore@opensolaris.org 		    kmem_zalloc(dnetp->max_rx_desc * sizeof (caddr_t),
22569860Sgdamore@opensolaris.org 		    KM_SLEEP);
22579860Sgdamore@opensolaris.org 		dnetp->rx_buf_paddr =
22589860Sgdamore@opensolaris.org 		    kmem_zalloc(dnetp->max_rx_desc * sizeof (uint32_t),
22599860Sgdamore@opensolaris.org 		    KM_SLEEP);
22609860Sgdamore@opensolaris.org 		/*
22619860Sgdamore@opensolaris.org 		 * Allocate or add to the pool of receive buffers.  The pool
22629860Sgdamore@opensolaris.org 		 * is shared among all instances of dnet.
22639860Sgdamore@opensolaris.org 		 *
22649860Sgdamore@opensolaris.org 		 * XXX NEEDSWORK
22659860Sgdamore@opensolaris.org 		 *
22669860Sgdamore@opensolaris.org 		 * We arbitrarily allocate twice as many receive buffers as
22679860Sgdamore@opensolaris.org 		 * receive descriptors because we use the buffers for streams
22689860Sgdamore@opensolaris.org 		 * messages to pass the packets up the stream.  We should
22699860Sgdamore@opensolaris.org 		 * instead have initialized constants reflecting
22709860Sgdamore@opensolaris.org 		 * MAX_RX_BUF_2104x and MAX_RX_BUF_2114x, and we should also
22719860Sgdamore@opensolaris.org 		 * probably have a total maximum for the free pool, so that we
22729860Sgdamore@opensolaris.org 		 * don't get out of hand when someone puts in an 8-port board.
22739860Sgdamore@opensolaris.org 		 * The maximum for the entire pool should be the total number
22749860Sgdamore@opensolaris.org 		 * of descriptors for all attached instances together, plus the
22759860Sgdamore@opensolaris.org 		 * total maximum for the free pool.  This maximum would only be
22769860Sgdamore@opensolaris.org 		 * reached after some number of instances allocate buffers:
22779860Sgdamore@opensolaris.org 		 * each instance would add (max_rx_buf-max_rx_desc) to the free
22789860Sgdamore@opensolaris.org 		 * pool.
22799860Sgdamore@opensolaris.org 		 */
22809860Sgdamore@opensolaris.org 		ndesc = dnetp->max_rx_desc - nrecv_desc_old;
22819860Sgdamore@opensolaris.org 		if ((ndesc > 0) &&
22829860Sgdamore@opensolaris.org 		    (dnet_rbuf_init(dnetp->devinfo, ndesc * 2) != 0))
22839860Sgdamore@opensolaris.org 			return (FAILURE);
22849860Sgdamore@opensolaris.org 
22859860Sgdamore@opensolaris.org 		for (i = 0; i < dnetp->max_rx_desc; i++) {
22869860Sgdamore@opensolaris.org 			struct rbuf_list *rp;
22879860Sgdamore@opensolaris.org 
22889860Sgdamore@opensolaris.org 			rp = dnet_rbuf_alloc(dnetp->devinfo, 1);
22899860Sgdamore@opensolaris.org 			if (rp == NULL)
22909860Sgdamore@opensolaris.org 				return (FAILURE);
22919860Sgdamore@opensolaris.org 			dnetp->rx_buf_vaddr[i] = rp->rbuf_vaddr;
22929860Sgdamore@opensolaris.org 			dnetp->rx_buf_paddr[i] = rp->rbuf_paddr;
22939860Sgdamore@opensolaris.org 		}
22949860Sgdamore@opensolaris.org 	}
22959860Sgdamore@opensolaris.org 
22969860Sgdamore@opensolaris.org 	return (SUCCESS);
22979860Sgdamore@opensolaris.org }
22989860Sgdamore@opensolaris.org /*
22999860Sgdamore@opensolaris.org  * free descriptors/buffers allocated for this device instance.  This routine
23009860Sgdamore@opensolaris.org  * should only be called while the device is reset.
23019860Sgdamore@opensolaris.org  */
23029860Sgdamore@opensolaris.org static void
dnet_free_bufs(struct dnetinstance * dnetp)230310340Sstallion@opensolaris.org dnet_free_bufs(struct dnetinstance *dnetp)
23049860Sgdamore@opensolaris.org {
23059860Sgdamore@opensolaris.org 	int i;
23069860Sgdamore@opensolaris.org 	/* free up any xmit descriptors/buffers */
23079860Sgdamore@opensolaris.org 	if (dnetp->tx_desc != NULL) {
23089860Sgdamore@opensolaris.org 		ddi_dma_mem_free(&dnetp->tx_desc_acchdl);
23099860Sgdamore@opensolaris.org 		dnetp->tx_desc = NULL;
23109860Sgdamore@opensolaris.org 		/* we use streams buffers for DMA in xmit process */
23119860Sgdamore@opensolaris.org 		if (dnetp->tx_msgbufp != NULL) {
23129860Sgdamore@opensolaris.org 			/* free up any streams message buffers unclaimed */
23139860Sgdamore@opensolaris.org 			for (i = 0; i < dnetp->nxmit_desc; i++) {
23149860Sgdamore@opensolaris.org 				if (dnetp->tx_msgbufp[i] != NULL) {
23159860Sgdamore@opensolaris.org 					freemsg(dnetp->tx_msgbufp[i]);
23169860Sgdamore@opensolaris.org 				}
23179860Sgdamore@opensolaris.org 			}
23189860Sgdamore@opensolaris.org 			kmem_free(dnetp->tx_msgbufp,
23199860Sgdamore@opensolaris.org 			    dnetp->nxmit_desc * sizeof (mblk_t **));
23209860Sgdamore@opensolaris.org 			dnetp->tx_msgbufp = NULL;
23219860Sgdamore@opensolaris.org 		}
23229860Sgdamore@opensolaris.org 		dnetp->nxmit_desc = 0;
23239860Sgdamore@opensolaris.org 	}
23249860Sgdamore@opensolaris.org 
23259860Sgdamore@opensolaris.org 	/* free up any receive descriptors/buffers */
23269860Sgdamore@opensolaris.org 	if (dnetp->rx_desc != NULL) {
23279860Sgdamore@opensolaris.org 		ddi_dma_mem_free(&dnetp->rx_desc_acchdl);
23289860Sgdamore@opensolaris.org 		dnetp->rx_desc = NULL;
23299860Sgdamore@opensolaris.org 		if (dnetp->rx_buf_vaddr != NULL) {
23309860Sgdamore@opensolaris.org 			/* free up the attached rbufs if any */
23319860Sgdamore@opensolaris.org 			for (i = 0; i < dnetp->nrecv_desc; i++) {
23329860Sgdamore@opensolaris.org 				if (dnetp->rx_buf_vaddr[i])
23339860Sgdamore@opensolaris.org 					dnet_rbuf_free(
23349860Sgdamore@opensolaris.org 					    (caddr_t)dnetp->rx_buf_vaddr[i]);
23359860Sgdamore@opensolaris.org 			}
23369860Sgdamore@opensolaris.org 			kmem_free(dnetp->rx_buf_vaddr,
23379860Sgdamore@opensolaris.org 			    dnetp->nrecv_desc * sizeof (caddr_t));
23389860Sgdamore@opensolaris.org 			kmem_free(dnetp->rx_buf_paddr,
23399860Sgdamore@opensolaris.org 			    dnetp->nrecv_desc * sizeof (uint32_t));
23409860Sgdamore@opensolaris.org 			dnetp->rx_buf_vaddr = NULL;
23419860Sgdamore@opensolaris.org 			dnetp->rx_buf_paddr = NULL;
23429860Sgdamore@opensolaris.org 		}
23439860Sgdamore@opensolaris.org 		dnetp->nrecv_desc = 0;
23449860Sgdamore@opensolaris.org 	}
23459860Sgdamore@opensolaris.org 
23469860Sgdamore@opensolaris.org 	if (dnetp->setup_buf_vaddr != NULL) {
23479860Sgdamore@opensolaris.org 		ddi_dma_mem_free(&dnetp->setup_buf_acchdl);
23489860Sgdamore@opensolaris.org 		dnetp->setup_buf_vaddr = NULL;
23499860Sgdamore@opensolaris.org 	}
23509860Sgdamore@opensolaris.org 
23519860Sgdamore@opensolaris.org 	if (dnetp->dma_handle != NULL) {
23529860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(dnetp->dma_handle);
23539860Sgdamore@opensolaris.org 		ddi_dma_free_handle(&dnetp->dma_handle);
23549860Sgdamore@opensolaris.org 		dnetp->dma_handle = NULL;
23559860Sgdamore@opensolaris.org 	}
23569860Sgdamore@opensolaris.org 
23579860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_tx != NULL) {
23589860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
23599860Sgdamore@opensolaris.org 		ddi_dma_free_handle(&dnetp->dma_handle_tx);
23609860Sgdamore@opensolaris.org 		dnetp->dma_handle_tx = NULL;
23619860Sgdamore@opensolaris.org 	}
23629860Sgdamore@opensolaris.org 
23639860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_txdesc != NULL) {
23649860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(dnetp->dma_handle_txdesc);
23659860Sgdamore@opensolaris.org 		ddi_dma_free_handle(&dnetp->dma_handle_txdesc);
23669860Sgdamore@opensolaris.org 		dnetp->dma_handle_txdesc = NULL;
23679860Sgdamore@opensolaris.org 	}
23689860Sgdamore@opensolaris.org 
23699860Sgdamore@opensolaris.org 	if (dnetp->dma_handle_setbuf != NULL) {
23709860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(dnetp->dma_handle_setbuf);
23719860Sgdamore@opensolaris.org 		ddi_dma_free_handle(&dnetp->dma_handle_setbuf);
23729860Sgdamore@opensolaris.org 		dnetp->dma_handle_setbuf = NULL;
23739860Sgdamore@opensolaris.org 	}
23749860Sgdamore@opensolaris.org 
23759860Sgdamore@opensolaris.org }
23769860Sgdamore@opensolaris.org 
23779860Sgdamore@opensolaris.org /*
23789860Sgdamore@opensolaris.org  * Initialize transmit and receive descriptors.
23799860Sgdamore@opensolaris.org  */
23809860Sgdamore@opensolaris.org static void
dnet_init_txrx_bufs(struct dnetinstance * dnetp)238110340Sstallion@opensolaris.org dnet_init_txrx_bufs(struct dnetinstance *dnetp)
23829860Sgdamore@opensolaris.org {
23839860Sgdamore@opensolaris.org 	int		i;
23849860Sgdamore@opensolaris.org 
23859860Sgdamore@opensolaris.org 	/*
23869860Sgdamore@opensolaris.org 	 * Initilize all the Tx descriptors
23879860Sgdamore@opensolaris.org 	 */
23889860Sgdamore@opensolaris.org 	for (i = 0; i < dnetp->nxmit_desc; i++) {
23899860Sgdamore@opensolaris.org 		/*
23909860Sgdamore@opensolaris.org 		 * We may be resetting the device due to errors,
23919860Sgdamore@opensolaris.org 		 * so free up any streams message buffer unclaimed.
23929860Sgdamore@opensolaris.org 		 */
23939860Sgdamore@opensolaris.org 		if (dnetp->tx_msgbufp[i] != NULL) {
23949860Sgdamore@opensolaris.org 			freemsg(dnetp->tx_msgbufp[i]);
23959860Sgdamore@opensolaris.org 			dnetp->tx_msgbufp[i] = NULL;
23969860Sgdamore@opensolaris.org 		}
23979860Sgdamore@opensolaris.org 		*(uint32_t *)&dnetp->tx_desc[i].desc0 = 0;
23989860Sgdamore@opensolaris.org 		*(uint32_t *)&dnetp->tx_desc[i].desc1 = 0;
23999860Sgdamore@opensolaris.org 		dnetp->tx_desc[i].buffer1 = 0;
24009860Sgdamore@opensolaris.org 		dnetp->tx_desc[i].buffer2 = 0;
24019860Sgdamore@opensolaris.org 	}
24029860Sgdamore@opensolaris.org 	dnetp->tx_desc[i - 1].desc1.end_of_ring = 1;
24039860Sgdamore@opensolaris.org 
24049860Sgdamore@opensolaris.org 	/*
24059860Sgdamore@opensolaris.org 	 * Initialize the Rx descriptors
24069860Sgdamore@opensolaris.org 	 */
24079860Sgdamore@opensolaris.org 	for (i = 0; i < dnetp->nrecv_desc; i++) {
24089860Sgdamore@opensolaris.org 		uint32_t end_paddr;
24099860Sgdamore@opensolaris.org 		*(uint32_t *)&dnetp->rx_desc[i].desc0 = 0;
24109860Sgdamore@opensolaris.org 		*(uint32_t *)&dnetp->rx_desc[i].desc1 = 0;
24119860Sgdamore@opensolaris.org 		dnetp->rx_desc[i].desc0.own = 1;
24129860Sgdamore@opensolaris.org 		dnetp->rx_desc[i].desc1.buffer_size1 = rx_buf_size;
24139860Sgdamore@opensolaris.org 		dnetp->rx_desc[i].buffer1 = dnetp->rx_buf_paddr[i];
24149860Sgdamore@opensolaris.org 		dnetp->rx_desc[i].buffer2 = 0;
24159860Sgdamore@opensolaris.org 		end_paddr = dnetp->rx_buf_paddr[i]+rx_buf_size-1;
24169860Sgdamore@opensolaris.org 
24179860Sgdamore@opensolaris.org 		if ((dnetp->rx_desc[i].buffer1 & ~dnetp->pgmask) !=
24189860Sgdamore@opensolaris.org 		    (end_paddr & ~dnetp->pgmask)) {
24199860Sgdamore@opensolaris.org 			/* discontiguous */
24209860Sgdamore@opensolaris.org 			dnetp->rx_desc[i].buffer2 = end_paddr&~dnetp->pgmask;
24219860Sgdamore@opensolaris.org 			dnetp->rx_desc[i].desc1.buffer_size2 =
24229860Sgdamore@opensolaris.org 			    (end_paddr & dnetp->pgmask) + 1;
24239860Sgdamore@opensolaris.org 			dnetp->rx_desc[i].desc1.buffer_size1 =
24249860Sgdamore@opensolaris.org 			    rx_buf_size-dnetp->rx_desc[i].desc1.buffer_size2;
24259860Sgdamore@opensolaris.org 		}
24269860Sgdamore@opensolaris.org 	}
24279860Sgdamore@opensolaris.org 	dnetp->rx_desc[i - 1].desc1.end_of_ring = 1;
24289860Sgdamore@opensolaris.org }
24299860Sgdamore@opensolaris.org 
24309860Sgdamore@opensolaris.org static int
alloc_descriptor(struct dnetinstance * dnetp)243110340Sstallion@opensolaris.org alloc_descriptor(struct dnetinstance *dnetp)
24329860Sgdamore@opensolaris.org {
24339860Sgdamore@opensolaris.org 	int index;
24349860Sgdamore@opensolaris.org 	struct tx_desc_type    *ring = dnetp->tx_desc;
24359860Sgdamore@opensolaris.org 
24369860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
24379860Sgdamore@opensolaris.org alloctop:
24389860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->txlock);
24399860Sgdamore@opensolaris.org 	index = dnetp->tx_current_desc;
24409860Sgdamore@opensolaris.org 
244110340Sstallion@opensolaris.org 	dnet_reclaim_Tx_desc(dnetp);
24429860Sgdamore@opensolaris.org 
24439860Sgdamore@opensolaris.org 	/* we do have free descriptors, right? */
24449860Sgdamore@opensolaris.org 	if (dnetp->free_desc <= 0) {
24459860Sgdamore@opensolaris.org #ifdef DNETDEBUG
24469860Sgdamore@opensolaris.org 		if (dnetdebug & DNETRECV)
24479860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "dnet: Ring buffer is full");
24489860Sgdamore@opensolaris.org #endif
24499860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
24509860Sgdamore@opensolaris.org 		return (FAILURE);
24519860Sgdamore@opensolaris.org 	}
24529860Sgdamore@opensolaris.org 
24539860Sgdamore@opensolaris.org 	/* sanity, make sure the next descriptor is free for use (should be) */
24549860Sgdamore@opensolaris.org 	if (ring[index].desc0.own) {
24559860Sgdamore@opensolaris.org #ifdef DNETDEBUG
24569860Sgdamore@opensolaris.org 		if (dnetdebug & DNETRECV)
24579860Sgdamore@opensolaris.org 			cmn_err(CE_WARN,
24589860Sgdamore@opensolaris.org 			    "dnet: next descriptor is not free for use");
24599860Sgdamore@opensolaris.org #endif
24609860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
24619860Sgdamore@opensolaris.org 		return (FAILURE);
24629860Sgdamore@opensolaris.org 	}
24639860Sgdamore@opensolaris.org 	if (dnetp->need_saddr) {
24649860Sgdamore@opensolaris.org 		mutex_exit(&dnetp->txlock);
24659860Sgdamore@opensolaris.org 		/* XXX function return value ignored */
24669860Sgdamore@opensolaris.org 		if (!dnetp->suspended)
246710340Sstallion@opensolaris.org 			(void) dnet_set_addr(dnetp);
24689860Sgdamore@opensolaris.org 		goto alloctop;
24699860Sgdamore@opensolaris.org 	}
24709860Sgdamore@opensolaris.org 
24719860Sgdamore@opensolaris.org 	*(uint32_t *)&ring[index].desc0 = 0;  /* init descs */
24729860Sgdamore@opensolaris.org 	*(uint32_t *)&ring[index].desc1 &= DNET_END_OF_RING;
24739860Sgdamore@opensolaris.org 
24749860Sgdamore@opensolaris.org 	/* hardware will own this descriptor when poll activated */
24759860Sgdamore@opensolaris.org 	dnetp->free_desc--;
24769860Sgdamore@opensolaris.org 
24779860Sgdamore@opensolaris.org 	/* point to next free descriptor to be used */
24789860Sgdamore@opensolaris.org 	dnetp->tx_current_desc = NextTXIndex(index);
24799860Sgdamore@opensolaris.org 
24809860Sgdamore@opensolaris.org #ifdef DNET_NOISY
24819860Sgdamore@opensolaris.org 	cmn_err(CE_WARN, "sfree 0x%x, transmitted 0x%x, tx_current 0x%x",
24829860Sgdamore@opensolaris.org 	    dnetp->free_desc, dnetp->transmitted_desc, dnetp->tx_current_desc);
24839860Sgdamore@opensolaris.org #endif
24849860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->txlock);
24859860Sgdamore@opensolaris.org 	return (SUCCESS);
24869860Sgdamore@opensolaris.org }
24879860Sgdamore@opensolaris.org 
24889860Sgdamore@opensolaris.org /*
24899860Sgdamore@opensolaris.org  * dnet_reclaim_Tx_desc() - called with txlock held.
24909860Sgdamore@opensolaris.org  */
24919860Sgdamore@opensolaris.org static void
dnet_reclaim_Tx_desc(struct dnetinstance * dnetp)249210340Sstallion@opensolaris.org dnet_reclaim_Tx_desc(struct dnetinstance *dnetp)
24939860Sgdamore@opensolaris.org {
24949860Sgdamore@opensolaris.org 	struct tx_desc_type	*desc = dnetp->tx_desc;
24959860Sgdamore@opensolaris.org 	int index;
24969860Sgdamore@opensolaris.org 
24979860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->txlock));
24989860Sgdamore@opensolaris.org 
24999860Sgdamore@opensolaris.org 	index = dnetp->transmitted_desc;
25009860Sgdamore@opensolaris.org 	while (((dnetp->free_desc == 0) || (index != dnetp->tx_current_desc)) &&
25019860Sgdamore@opensolaris.org 	    !(desc[index].desc0.own)) {
25029860Sgdamore@opensolaris.org 		/*
25039860Sgdamore@opensolaris.org 		 * Check for Tx Error that gets set
25049860Sgdamore@opensolaris.org 		 * in the last desc.
25059860Sgdamore@opensolaris.org 		 */
25069860Sgdamore@opensolaris.org 		if (desc[index].desc1.setup_packet == 0 &&
25079860Sgdamore@opensolaris.org 		    desc[index].desc1.last_desc &&
25089860Sgdamore@opensolaris.org 		    desc[index].desc0.err_summary)
250910340Sstallion@opensolaris.org 			update_tx_stats(dnetp, index);
25109860Sgdamore@opensolaris.org 
25119860Sgdamore@opensolaris.org 		/*
25129860Sgdamore@opensolaris.org 		 * If we have used the streams message buffer for this
25139860Sgdamore@opensolaris.org 		 * descriptor then free up the message now.
25149860Sgdamore@opensolaris.org 		 */
25159860Sgdamore@opensolaris.org 		if (dnetp->tx_msgbufp[index] != NULL) {
25169860Sgdamore@opensolaris.org 			freemsg(dnetp->tx_msgbufp[index]);
25179860Sgdamore@opensolaris.org 			dnetp->tx_msgbufp[index] = NULL;
25189860Sgdamore@opensolaris.org 		}
25199860Sgdamore@opensolaris.org 		dnetp->free_desc++;
25209860Sgdamore@opensolaris.org 		index = (index+1) % dnetp->max_tx_desc;
25219860Sgdamore@opensolaris.org 	}
25229860Sgdamore@opensolaris.org 
25239860Sgdamore@opensolaris.org 	dnetp->transmitted_desc = index;
25249860Sgdamore@opensolaris.org }
25259860Sgdamore@opensolaris.org 
25269860Sgdamore@opensolaris.org /*
25279860Sgdamore@opensolaris.org  * Receive buffer allocation/freeing routines.
25289860Sgdamore@opensolaris.org  *
25299860Sgdamore@opensolaris.org  * There is a common pool of receive buffers shared by all dnet instances.
25309860Sgdamore@opensolaris.org  *
25319860Sgdamore@opensolaris.org  * XXX NEEDSWORK
25329860Sgdamore@opensolaris.org  *
25339860Sgdamore@opensolaris.org  * We arbitrarily allocate twice as many receive buffers as
25349860Sgdamore@opensolaris.org  * receive descriptors because we use the buffers for streams
25359860Sgdamore@opensolaris.org  * messages to pass the packets up the stream.  We should
25369860Sgdamore@opensolaris.org  * instead have initialized constants reflecting
25379860Sgdamore@opensolaris.org  * MAX_RX_BUF_2104x and MAX_RX_BUF_2114x, and we should also
25389860Sgdamore@opensolaris.org  * probably have a total maximum for the free pool, so that we
25399860Sgdamore@opensolaris.org  * don't get out of hand when someone puts in an 8-port board.
25409860Sgdamore@opensolaris.org  * The maximum for the entire pool should be the total number
25419860Sgdamore@opensolaris.org  * of descriptors for all attached instances together, plus the
25429860Sgdamore@opensolaris.org  * total maximum for the free pool.  This maximum would only be
25439860Sgdamore@opensolaris.org  * reached after some number of instances allocate buffers:
25449860Sgdamore@opensolaris.org  * each instance would add (max_rx_buf-max_rx_desc) to the free
25459860Sgdamore@opensolaris.org  * pool.
25469860Sgdamore@opensolaris.org  */
25479860Sgdamore@opensolaris.org 
25489860Sgdamore@opensolaris.org static struct rbuf_list *rbuf_usedlist_head;
25499860Sgdamore@opensolaris.org static struct rbuf_list *rbuf_freelist_head;
25509860Sgdamore@opensolaris.org static struct rbuf_list *rbuf_usedlist_end;	/* last buffer allocated */
25519860Sgdamore@opensolaris.org 
25529860Sgdamore@opensolaris.org static int rbuf_freebufs;	/* no. of free buffers in the pool */
25539860Sgdamore@opensolaris.org static int rbuf_pool_size;	/* total no. of buffers in the pool */
25549860Sgdamore@opensolaris.org 
25559860Sgdamore@opensolaris.org /* initialize/add 'nbufs' buffers to the rbuf pool */
25569860Sgdamore@opensolaris.org /* ARGSUSED */
25579860Sgdamore@opensolaris.org static int
dnet_rbuf_init(dev_info_t * dip,int nbufs)25589860Sgdamore@opensolaris.org dnet_rbuf_init(dev_info_t *dip, int nbufs)
25599860Sgdamore@opensolaris.org {
25609860Sgdamore@opensolaris.org 	int i;
25619860Sgdamore@opensolaris.org 	struct rbuf_list *rp;
25629860Sgdamore@opensolaris.org 	ddi_dma_cookie_t cookie;
25639860Sgdamore@opensolaris.org 	uint_t ncookies;
25649860Sgdamore@opensolaris.org 	size_t len;
25659860Sgdamore@opensolaris.org 
25669860Sgdamore@opensolaris.org 	mutex_enter(&dnet_rbuf_lock);
25679860Sgdamore@opensolaris.org 
25689860Sgdamore@opensolaris.org 	/* allocate buffers and add them to the pool */
25699860Sgdamore@opensolaris.org 	for (i = 0; i < nbufs; i++) {
25709860Sgdamore@opensolaris.org 		/* allocate rbuf_list element */
25719860Sgdamore@opensolaris.org 		rp = kmem_zalloc(sizeof (struct rbuf_list), KM_SLEEP);
25729860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dip, &dma_attr_rb, DDI_DMA_SLEEP,
25739860Sgdamore@opensolaris.org 		    0, &rp->rbuf_dmahdl) != DDI_SUCCESS)
25749860Sgdamore@opensolaris.org 			goto fail_kfree;
25759860Sgdamore@opensolaris.org 
25769860Sgdamore@opensolaris.org 		/* allocate dma memory for the buffer */
25779860Sgdamore@opensolaris.org 		if (ddi_dma_mem_alloc(rp->rbuf_dmahdl, rx_buf_size, &accattr,
25789860Sgdamore@opensolaris.org 		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
25799860Sgdamore@opensolaris.org 		    &rp->rbuf_vaddr, &len,
25809860Sgdamore@opensolaris.org 		    &rp->rbuf_acchdl) != DDI_SUCCESS)
25819860Sgdamore@opensolaris.org 			goto fail_freehdl;
25829860Sgdamore@opensolaris.org 
25839860Sgdamore@opensolaris.org 		if (ddi_dma_addr_bind_handle(rp->rbuf_dmahdl, NULL,
25849860Sgdamore@opensolaris.org 		    rp->rbuf_vaddr, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
25859860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, NULL, &cookie,
25869860Sgdamore@opensolaris.org 		    &ncookies) != DDI_DMA_MAPPED)
25879860Sgdamore@opensolaris.org 			goto fail_free;
25889860Sgdamore@opensolaris.org 
25899860Sgdamore@opensolaris.org 		if (ncookies > 2)
25909860Sgdamore@opensolaris.org 			goto fail_unbind;
25919860Sgdamore@opensolaris.org 		if (ncookies == 1) {
25929860Sgdamore@opensolaris.org 			rp->rbuf_endpaddr =
25939860Sgdamore@opensolaris.org 			    cookie.dmac_address + rx_buf_size - 1;
25949860Sgdamore@opensolaris.org 		} else {
25959860Sgdamore@opensolaris.org 			ddi_dma_nextcookie(rp->rbuf_dmahdl, &cookie);
25969860Sgdamore@opensolaris.org 			rp->rbuf_endpaddr =
25979860Sgdamore@opensolaris.org 			    cookie.dmac_address + cookie.dmac_size - 1;
25989860Sgdamore@opensolaris.org 		}
25999860Sgdamore@opensolaris.org 		rp->rbuf_paddr = cookie.dmac_address;
26009860Sgdamore@opensolaris.org 
26019860Sgdamore@opensolaris.org 		rp->rbuf_next = rbuf_freelist_head;
26029860Sgdamore@opensolaris.org 		rbuf_freelist_head = rp;
26039860Sgdamore@opensolaris.org 		rbuf_pool_size++;
26049860Sgdamore@opensolaris.org 		rbuf_freebufs++;
26059860Sgdamore@opensolaris.org 	}
26069860Sgdamore@opensolaris.org 
26079860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
26089860Sgdamore@opensolaris.org 	return (0);
26099860Sgdamore@opensolaris.org fail_unbind:
26109860Sgdamore@opensolaris.org 	(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
26119860Sgdamore@opensolaris.org fail_free:
26129860Sgdamore@opensolaris.org 	ddi_dma_mem_free(&rp->rbuf_acchdl);
26139860Sgdamore@opensolaris.org fail_freehdl:
26149860Sgdamore@opensolaris.org 	ddi_dma_free_handle(&rp->rbuf_dmahdl);
26159860Sgdamore@opensolaris.org fail_kfree:
26169860Sgdamore@opensolaris.org 	kmem_free(rp, sizeof (struct rbuf_list));
26179860Sgdamore@opensolaris.org 
26189860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
26199860Sgdamore@opensolaris.org 	return (-1);
26209860Sgdamore@opensolaris.org }
26219860Sgdamore@opensolaris.org 
26229860Sgdamore@opensolaris.org /*
26239860Sgdamore@opensolaris.org  * Try to free up all the rbufs in the pool. Returns 0 if it frees up all
26249860Sgdamore@opensolaris.org  * buffers. The buffers in the used list are considered busy so these
26259860Sgdamore@opensolaris.org  * buffers are not freed.
26269860Sgdamore@opensolaris.org  */
26279860Sgdamore@opensolaris.org static int
dnet_rbuf_destroy()26289860Sgdamore@opensolaris.org dnet_rbuf_destroy()
26299860Sgdamore@opensolaris.org {
26309860Sgdamore@opensolaris.org 	struct rbuf_list *rp, *next;
26319860Sgdamore@opensolaris.org 
26329860Sgdamore@opensolaris.org 	mutex_enter(&dnet_rbuf_lock);
26339860Sgdamore@opensolaris.org 
26349860Sgdamore@opensolaris.org 	for (rp = rbuf_freelist_head; rp; rp = next) {
26359860Sgdamore@opensolaris.org 		next = rp->rbuf_next;
26369860Sgdamore@opensolaris.org 		ddi_dma_mem_free(&rp->rbuf_acchdl);
26379860Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
26389860Sgdamore@opensolaris.org 		kmem_free(rp, sizeof (struct rbuf_list));
26399860Sgdamore@opensolaris.org 		rbuf_pool_size--;
26409860Sgdamore@opensolaris.org 		rbuf_freebufs--;
26419860Sgdamore@opensolaris.org 	}
26429860Sgdamore@opensolaris.org 	rbuf_freelist_head = NULL;
26439860Sgdamore@opensolaris.org 
26449860Sgdamore@opensolaris.org 	if (rbuf_pool_size) { /* pool is still not empty */
26459860Sgdamore@opensolaris.org 		mutex_exit(&dnet_rbuf_lock);
26469860Sgdamore@opensolaris.org 		return (-1);
26479860Sgdamore@opensolaris.org 	}
26489860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
26499860Sgdamore@opensolaris.org 	return (0);
26509860Sgdamore@opensolaris.org }
26519860Sgdamore@opensolaris.org static struct rbuf_list *
dnet_rbuf_alloc(dev_info_t * dip,int cansleep)26529860Sgdamore@opensolaris.org dnet_rbuf_alloc(dev_info_t *dip, int cansleep)
26539860Sgdamore@opensolaris.org {
26549860Sgdamore@opensolaris.org 	struct rbuf_list *rp;
26559860Sgdamore@opensolaris.org 	size_t len;
26569860Sgdamore@opensolaris.org 	ddi_dma_cookie_t cookie;
26579860Sgdamore@opensolaris.org 	uint_t ncookies;
26589860Sgdamore@opensolaris.org 
26599860Sgdamore@opensolaris.org 	mutex_enter(&dnet_rbuf_lock);
26609860Sgdamore@opensolaris.org 
26619860Sgdamore@opensolaris.org 	if (rbuf_freelist_head == NULL) {
26629860Sgdamore@opensolaris.org 
26639860Sgdamore@opensolaris.org 		if (!cansleep) {
26649860Sgdamore@opensolaris.org 			mutex_exit(&dnet_rbuf_lock);
26659860Sgdamore@opensolaris.org 			return (NULL);
26669860Sgdamore@opensolaris.org 		}
26679860Sgdamore@opensolaris.org 
26689860Sgdamore@opensolaris.org 		/* allocate rbuf_list element */
26699860Sgdamore@opensolaris.org 		rp = kmem_zalloc(sizeof (struct rbuf_list), KM_SLEEP);
26709860Sgdamore@opensolaris.org 		if (ddi_dma_alloc_handle(dip, &dma_attr_rb, DDI_DMA_SLEEP,
26719860Sgdamore@opensolaris.org 		    0, &rp->rbuf_dmahdl) != DDI_SUCCESS)
26729860Sgdamore@opensolaris.org 			goto fail_kfree;
26739860Sgdamore@opensolaris.org 
26749860Sgdamore@opensolaris.org 		/* allocate dma memory for the buffer */
26759860Sgdamore@opensolaris.org 		if (ddi_dma_mem_alloc(rp->rbuf_dmahdl, rx_buf_size, &accattr,
26769860Sgdamore@opensolaris.org 		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
26779860Sgdamore@opensolaris.org 		    &rp->rbuf_vaddr, &len,
26789860Sgdamore@opensolaris.org 		    &rp->rbuf_acchdl) != DDI_SUCCESS)
26799860Sgdamore@opensolaris.org 			goto fail_freehdl;
26809860Sgdamore@opensolaris.org 
26819860Sgdamore@opensolaris.org 		if (ddi_dma_addr_bind_handle(rp->rbuf_dmahdl, NULL,
26829860Sgdamore@opensolaris.org 		    rp->rbuf_vaddr, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
26839860Sgdamore@opensolaris.org 		    DDI_DMA_SLEEP, NULL, &cookie,
26849860Sgdamore@opensolaris.org 		    &ncookies) != DDI_DMA_MAPPED)
26859860Sgdamore@opensolaris.org 			goto fail_free;
26869860Sgdamore@opensolaris.org 
26879860Sgdamore@opensolaris.org 		if (ncookies > 2)
26889860Sgdamore@opensolaris.org 			goto fail_unbind;
26899860Sgdamore@opensolaris.org 		if (ncookies == 1) {
26909860Sgdamore@opensolaris.org 			rp->rbuf_endpaddr =
26919860Sgdamore@opensolaris.org 			    cookie.dmac_address + rx_buf_size - 1;
26929860Sgdamore@opensolaris.org 		} else {
26939860Sgdamore@opensolaris.org 			ddi_dma_nextcookie(rp->rbuf_dmahdl, &cookie);
26949860Sgdamore@opensolaris.org 			rp->rbuf_endpaddr =
26959860Sgdamore@opensolaris.org 			    cookie.dmac_address + cookie.dmac_size - 1;
26969860Sgdamore@opensolaris.org 		}
26979860Sgdamore@opensolaris.org 		rp->rbuf_paddr = cookie.dmac_address;
26989860Sgdamore@opensolaris.org 
26999860Sgdamore@opensolaris.org 		rbuf_freelist_head = rp;
27009860Sgdamore@opensolaris.org 		rbuf_pool_size++;
27019860Sgdamore@opensolaris.org 		rbuf_freebufs++;
27029860Sgdamore@opensolaris.org 	}
27039860Sgdamore@opensolaris.org 
27049860Sgdamore@opensolaris.org 	/* take the buffer from the head of the free list */
27059860Sgdamore@opensolaris.org 	rp = rbuf_freelist_head;
27069860Sgdamore@opensolaris.org 	rbuf_freelist_head = rbuf_freelist_head->rbuf_next;
27079860Sgdamore@opensolaris.org 
27089860Sgdamore@opensolaris.org 	/* update the used list; put the entry at the end */
27099860Sgdamore@opensolaris.org 	if (rbuf_usedlist_head == NULL)
27109860Sgdamore@opensolaris.org 		rbuf_usedlist_head = rp;
27119860Sgdamore@opensolaris.org 	else
27129860Sgdamore@opensolaris.org 		rbuf_usedlist_end->rbuf_next = rp;
27139860Sgdamore@opensolaris.org 	rp->rbuf_next = NULL;
27149860Sgdamore@opensolaris.org 	rbuf_usedlist_end = rp;
27159860Sgdamore@opensolaris.org 	rbuf_freebufs--;
27169860Sgdamore@opensolaris.org 
27179860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
27189860Sgdamore@opensolaris.org 
27199860Sgdamore@opensolaris.org 	return (rp);
27209860Sgdamore@opensolaris.org fail_unbind:
27219860Sgdamore@opensolaris.org 	(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
27229860Sgdamore@opensolaris.org fail_free:
27239860Sgdamore@opensolaris.org 	ddi_dma_mem_free(&rp->rbuf_acchdl);
27249860Sgdamore@opensolaris.org fail_freehdl:
27259860Sgdamore@opensolaris.org 	ddi_dma_free_handle(&rp->rbuf_dmahdl);
27269860Sgdamore@opensolaris.org fail_kfree:
27279860Sgdamore@opensolaris.org 	kmem_free(rp, sizeof (struct rbuf_list));
27289860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
27299860Sgdamore@opensolaris.org 	return (NULL);
27309860Sgdamore@opensolaris.org }
27319860Sgdamore@opensolaris.org 
27329860Sgdamore@opensolaris.org static void
dnet_rbuf_free(caddr_t vaddr)27339860Sgdamore@opensolaris.org dnet_rbuf_free(caddr_t vaddr)
27349860Sgdamore@opensolaris.org {
27359860Sgdamore@opensolaris.org 	struct rbuf_list *rp, *prev;
27369860Sgdamore@opensolaris.org 
27379860Sgdamore@opensolaris.org 	ASSERT(vaddr != NULL);
27389860Sgdamore@opensolaris.org 	ASSERT(rbuf_usedlist_head != NULL);
27399860Sgdamore@opensolaris.org 
27409860Sgdamore@opensolaris.org 	mutex_enter(&dnet_rbuf_lock);
27419860Sgdamore@opensolaris.org 
27429860Sgdamore@opensolaris.org 	/* find the entry in the used list */
27439860Sgdamore@opensolaris.org 	for (prev = rp = rbuf_usedlist_head; rp; rp = rp->rbuf_next) {
27449860Sgdamore@opensolaris.org 		if (rp->rbuf_vaddr == vaddr)
27459860Sgdamore@opensolaris.org 			break;
27469860Sgdamore@opensolaris.org 		prev = rp;
27479860Sgdamore@opensolaris.org 	}
27489860Sgdamore@opensolaris.org 
27499860Sgdamore@opensolaris.org 	if (rp == NULL) {
27509860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "DNET: rbuf_free: bad addr 0x%p",
27519860Sgdamore@opensolaris.org 		    (void *)vaddr);
27529860Sgdamore@opensolaris.org 		mutex_exit(&dnet_rbuf_lock);
27539860Sgdamore@opensolaris.org 		return;
27549860Sgdamore@opensolaris.org 	}
27559860Sgdamore@opensolaris.org 
27569860Sgdamore@opensolaris.org 	/* update the used list and put the buffer back in the free list */
27579860Sgdamore@opensolaris.org 	if (rbuf_usedlist_head != rp) {
27589860Sgdamore@opensolaris.org 		prev->rbuf_next = rp->rbuf_next;
27599860Sgdamore@opensolaris.org 		if (rbuf_usedlist_end == rp)
27609860Sgdamore@opensolaris.org 			rbuf_usedlist_end = prev;
27619860Sgdamore@opensolaris.org 	} else {
27629860Sgdamore@opensolaris.org 		rbuf_usedlist_head = rp->rbuf_next;
27639860Sgdamore@opensolaris.org 		if (rbuf_usedlist_end == rp)
27649860Sgdamore@opensolaris.org 			rbuf_usedlist_end = NULL;
27659860Sgdamore@opensolaris.org 	}
27669860Sgdamore@opensolaris.org 	rp->rbuf_next = rbuf_freelist_head;
27679860Sgdamore@opensolaris.org 	rbuf_freelist_head = rp;
27689860Sgdamore@opensolaris.org 	rbuf_freebufs++;
27699860Sgdamore@opensolaris.org 
27709860Sgdamore@opensolaris.org 	mutex_exit(&dnet_rbuf_lock);
27719860Sgdamore@opensolaris.org }
27729860Sgdamore@opensolaris.org 
27739860Sgdamore@opensolaris.org /*
27749860Sgdamore@opensolaris.org  * Free the receive buffer used in a stream's message block allocated
27759860Sgdamore@opensolaris.org  * thru desballoc().
27769860Sgdamore@opensolaris.org  */
27779860Sgdamore@opensolaris.org static void
dnet_freemsg_buf(struct free_ptr * frp)27789860Sgdamore@opensolaris.org dnet_freemsg_buf(struct free_ptr *frp)
27799860Sgdamore@opensolaris.org {
27809860Sgdamore@opensolaris.org 	dnet_rbuf_free((caddr_t)frp->buf); /* buffer goes back to the pool */
27819860Sgdamore@opensolaris.org 	kmem_free(frp, sizeof (*frp)); /* free up the free_rtn structure */
27829860Sgdamore@opensolaris.org }
27839860Sgdamore@opensolaris.org 
27849860Sgdamore@opensolaris.org /*
27859860Sgdamore@opensolaris.org  *	========== SROM Read Routines ==========
27869860Sgdamore@opensolaris.org  */
27879860Sgdamore@opensolaris.org 
27889860Sgdamore@opensolaris.org /*
27899860Sgdamore@opensolaris.org  * The following code gets the SROM information, either by reading it
27909860Sgdamore@opensolaris.org  * from the device or, failing that, by reading a property.
27919860Sgdamore@opensolaris.org  */
27929860Sgdamore@opensolaris.org static int
dnet_read_srom(dev_info_t * devinfo,int board_type,ddi_acc_handle_t io_handle,caddr_t io_reg,uchar_t * vi,int maxlen)27939860Sgdamore@opensolaris.org dnet_read_srom(dev_info_t *devinfo, int board_type, ddi_acc_handle_t io_handle,
27949860Sgdamore@opensolaris.org     caddr_t io_reg, uchar_t *vi, int maxlen)
27959860Sgdamore@opensolaris.org {
27969860Sgdamore@opensolaris.org 	int all_ones, zerocheck, i;
27979860Sgdamore@opensolaris.org 
27989860Sgdamore@opensolaris.org 	/*
27999860Sgdamore@opensolaris.org 	 * Load SROM into vendor_info
28009860Sgdamore@opensolaris.org 	 */
28019860Sgdamore@opensolaris.org 	if (board_type == DEVICE_ID_21040)
28029860Sgdamore@opensolaris.org 		dnet_read21040addr(devinfo, io_handle, io_reg, vi, &maxlen);
28039860Sgdamore@opensolaris.org 	else
28049860Sgdamore@opensolaris.org 		/* 21041/21140 serial rom */
28059860Sgdamore@opensolaris.org 		dnet_read21140srom(io_handle, io_reg, vi, maxlen);
28069860Sgdamore@opensolaris.org 	/*
28079860Sgdamore@opensolaris.org 	 * If the dumpsrom property is present in the conf file, print
28089860Sgdamore@opensolaris.org 	 * the contents of the SROM to the console
28099860Sgdamore@opensolaris.org 	 */
28109860Sgdamore@opensolaris.org 	if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
28119860Sgdamore@opensolaris.org 	    "dumpsrom", 0))
28129860Sgdamore@opensolaris.org 		dnet_dumpbin("SROM", vi, 1, maxlen);
28139860Sgdamore@opensolaris.org 
28149860Sgdamore@opensolaris.org 	for (zerocheck = i = 0, all_ones = 0xff; i < maxlen; i++) {
28159860Sgdamore@opensolaris.org 		zerocheck |= vi[i];
28169860Sgdamore@opensolaris.org 		all_ones &= vi[i];
28179860Sgdamore@opensolaris.org 	}
28189860Sgdamore@opensolaris.org 	if (zerocheck == 0 || all_ones == 0xff) {
28199860Sgdamore@opensolaris.org 		return (get_alternative_srom_image(devinfo, vi, maxlen));
28209860Sgdamore@opensolaris.org 	} else {
28219860Sgdamore@opensolaris.org #ifdef BUG_4010796
28229860Sgdamore@opensolaris.org 		set_alternative_srom_image(devinfo, vi, maxlen);
28239860Sgdamore@opensolaris.org #endif
28249860Sgdamore@opensolaris.org 		return (0);	/* Primary */
28259860Sgdamore@opensolaris.org 	}
28269860Sgdamore@opensolaris.org }
28279860Sgdamore@opensolaris.org 
28289860Sgdamore@opensolaris.org /*
28299860Sgdamore@opensolaris.org  * The function reads the ethernet address of the 21040 adapter
28309860Sgdamore@opensolaris.org  */
28319860Sgdamore@opensolaris.org static void
dnet_read21040addr(dev_info_t * dip,ddi_acc_handle_t io_handle,caddr_t io_reg,uchar_t * addr,int * len)28329860Sgdamore@opensolaris.org dnet_read21040addr(dev_info_t *dip, ddi_acc_handle_t io_handle, caddr_t io_reg,
28339860Sgdamore@opensolaris.org     uchar_t *addr, int *len)
28349860Sgdamore@opensolaris.org {
28359860Sgdamore@opensolaris.org 	uint32_t	val;
28369860Sgdamore@opensolaris.org 	int		i;
28379860Sgdamore@opensolaris.org 
28389860Sgdamore@opensolaris.org 	/* No point reading more than the ethernet address */
28399860Sgdamore@opensolaris.org 	*len = ddi_getprop(DDI_DEV_T_ANY, dip,
28409860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, macoffset_propname, 0) + ETHERADDRL;
28419860Sgdamore@opensolaris.org 
28429860Sgdamore@opensolaris.org 	/* Reset ROM pointer */
28439860Sgdamore@opensolaris.org 	ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG), 0);
28449860Sgdamore@opensolaris.org 	for (i = 0; i < *len; i++) {
28459860Sgdamore@opensolaris.org 		do {
28469860Sgdamore@opensolaris.org 			val = ddi_get32(io_handle,
28479860Sgdamore@opensolaris.org 			    REG32(io_reg, ETHER_ROM_REG));
28489860Sgdamore@opensolaris.org 		} while (val & 0x80000000);
28499860Sgdamore@opensolaris.org 		addr[i] = val & 0xFF;
28509860Sgdamore@opensolaris.org 	}
28519860Sgdamore@opensolaris.org }
28529860Sgdamore@opensolaris.org 
28539860Sgdamore@opensolaris.org #define	drv_nsecwait(x)	drv_usecwait(((x)+999)/1000) /* XXX */
28549860Sgdamore@opensolaris.org 
28559860Sgdamore@opensolaris.org /*
28569860Sgdamore@opensolaris.org  * The function reads the SROM	of the 21140 adapter
28579860Sgdamore@opensolaris.org  */
28589860Sgdamore@opensolaris.org static void
dnet_read21140srom(ddi_acc_handle_t io_handle,caddr_t io_reg,uchar_t * addr,int maxlen)28599860Sgdamore@opensolaris.org dnet_read21140srom(ddi_acc_handle_t io_handle, caddr_t io_reg, uchar_t *addr,
28609860Sgdamore@opensolaris.org     int maxlen)
28619860Sgdamore@opensolaris.org {
28629860Sgdamore@opensolaris.org 	uint32_t 	i, j;
28639860Sgdamore@opensolaris.org 	uint32_t	dout;
28649860Sgdamore@opensolaris.org 	uint16_t	word;
28659860Sgdamore@opensolaris.org 	uint8_t		rom_addr;
28669860Sgdamore@opensolaris.org 	uint8_t		bit;
28679860Sgdamore@opensolaris.org 
28689860Sgdamore@opensolaris.org 
28699860Sgdamore@opensolaris.org 	rom_addr = 0;
28709860Sgdamore@opensolaris.org 	for (i = 0; i <	maxlen; i += 2) {
28719860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28729860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM);
28739860Sgdamore@opensolaris.org 		drv_nsecwait(30);
28749860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28759860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP);
28769860Sgdamore@opensolaris.org 		drv_nsecwait(50);
28779860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28789860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
28799860Sgdamore@opensolaris.org 		drv_nsecwait(250);
28809860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28819860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP);
28829860Sgdamore@opensolaris.org 		drv_nsecwait(100);
28839860Sgdamore@opensolaris.org 
28849860Sgdamore@opensolaris.org 		/* command */
28859860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28869860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
28879860Sgdamore@opensolaris.org 		drv_nsecwait(150);
28889860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28899860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | DATA_IN | SEL_CLK);
28909860Sgdamore@opensolaris.org 		drv_nsecwait(250);
28919860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28929860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
28939860Sgdamore@opensolaris.org 		drv_nsecwait(250);
28949860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28959860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | DATA_IN | SEL_CLK);
28969860Sgdamore@opensolaris.org 		drv_nsecwait(250);
28979860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
28989860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
28999860Sgdamore@opensolaris.org 		drv_nsecwait(100);
29009860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29019860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP);
29029860Sgdamore@opensolaris.org 		drv_nsecwait(150);
29039860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29049860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
29059860Sgdamore@opensolaris.org 		drv_nsecwait(250);
29069860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29079860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM | SEL_CHIP);
29089860Sgdamore@opensolaris.org 		drv_nsecwait(100);
29099860Sgdamore@opensolaris.org 
29109860Sgdamore@opensolaris.org 		/* Address */
29119860Sgdamore@opensolaris.org 		for (j = HIGH_ADDRESS_BIT; j >= 1; j >>= 1) {
29129860Sgdamore@opensolaris.org 			bit = (rom_addr & j) ? DATA_IN : 0;
29139860Sgdamore@opensolaris.org 			ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29149860Sgdamore@opensolaris.org 			    READ_OP | SEL_ROM | SEL_CHIP | bit);
29159860Sgdamore@opensolaris.org 			drv_nsecwait(150);
29169860Sgdamore@opensolaris.org 			ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29179860Sgdamore@opensolaris.org 			    READ_OP | SEL_ROM | SEL_CHIP | bit | SEL_CLK);
29189860Sgdamore@opensolaris.org 			drv_nsecwait(250);
29199860Sgdamore@opensolaris.org 			ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29209860Sgdamore@opensolaris.org 			    READ_OP | SEL_ROM | SEL_CHIP | bit);
29219860Sgdamore@opensolaris.org 			drv_nsecwait(100);
29229860Sgdamore@opensolaris.org 		}
29239860Sgdamore@opensolaris.org 		drv_nsecwait(150);
29249860Sgdamore@opensolaris.org 
29259860Sgdamore@opensolaris.org 		/* Data */
29269860Sgdamore@opensolaris.org 		word = 0;
29279860Sgdamore@opensolaris.org 		for (j = 0x8000; j >= 1; j >>= 1) {
29289860Sgdamore@opensolaris.org 			ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29299860Sgdamore@opensolaris.org 			    READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
29309860Sgdamore@opensolaris.org 			drv_nsecwait(100);
29319860Sgdamore@opensolaris.org 			dout = ddi_get32(io_handle,
29329860Sgdamore@opensolaris.org 			    REG32(io_reg, ETHER_ROM_REG));
29339860Sgdamore@opensolaris.org 			drv_nsecwait(150);
29349860Sgdamore@opensolaris.org 			if (dout & DATA_OUT)
29359860Sgdamore@opensolaris.org 				word |= j;
29369860Sgdamore@opensolaris.org 			ddi_put32(io_handle,
29379860Sgdamore@opensolaris.org 			    REG32(io_reg, ETHER_ROM_REG),
29389860Sgdamore@opensolaris.org 			    READ_OP | SEL_ROM | SEL_CHIP);
29399860Sgdamore@opensolaris.org 			drv_nsecwait(250);
29409860Sgdamore@opensolaris.org 		}
29419860Sgdamore@opensolaris.org 		addr[i] = (word & 0x0000FF);
29429860Sgdamore@opensolaris.org 		addr[i + 1] = (word >> 8);
29439860Sgdamore@opensolaris.org 		rom_addr++;
29449860Sgdamore@opensolaris.org 		ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
29459860Sgdamore@opensolaris.org 		    READ_OP | SEL_ROM);
29469860Sgdamore@opensolaris.org 		drv_nsecwait(100);
29479860Sgdamore@opensolaris.org 	}
29489860Sgdamore@opensolaris.org }
29499860Sgdamore@opensolaris.org 
29509860Sgdamore@opensolaris.org 
29519860Sgdamore@opensolaris.org /*
29529860Sgdamore@opensolaris.org  * XXX NEEDSWORK
29539860Sgdamore@opensolaris.org  *
29549860Sgdamore@opensolaris.org  * Some lame multiport cards have only one SROM, which can be accessed
29559860Sgdamore@opensolaris.org  * only from the "first" 21x4x chip, whichever that one is.  If we can't
29569860Sgdamore@opensolaris.org  * get at our SROM, we look for its contents in a property instead, which
29579860Sgdamore@opensolaris.org  * we rely on the bootstrap to have properly set.
29589860Sgdamore@opensolaris.org  * #ifdef BUG_4010796
29599860Sgdamore@opensolaris.org  * We also have a hack to try to set it ourselves, when the "first" port
29609860Sgdamore@opensolaris.org  * attaches, if it has not already been properly set.  However, this method
29619860Sgdamore@opensolaris.org  * is not reliable, since it makes the unwarrented assumption that the
29629860Sgdamore@opensolaris.org  * "first" port will attach first.
29639860Sgdamore@opensolaris.org  * #endif
29649860Sgdamore@opensolaris.org  */
29659860Sgdamore@opensolaris.org 
29669860Sgdamore@opensolaris.org static int
get_alternative_srom_image(dev_info_t * devinfo,uchar_t * vi,int len)29679860Sgdamore@opensolaris.org get_alternative_srom_image(dev_info_t *devinfo, uchar_t *vi, int len)
29689860Sgdamore@opensolaris.org {
29699860Sgdamore@opensolaris.org 	int	l = len;
29709860Sgdamore@opensolaris.org 
29719860Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
29729860Sgdamore@opensolaris.org 	    "DNET_SROM", (caddr_t)vi, &len) != DDI_PROP_SUCCESS &&
29739860Sgdamore@opensolaris.org 	    (len = l) && ddi_getlongprop_buf(DDI_DEV_T_ANY,
29749860Sgdamore@opensolaris.org 	    ddi_get_parent(devinfo), DDI_PROP_DONTPASS, "DNET_SROM",
29759860Sgdamore@opensolaris.org 	    (caddr_t)vi, &len) != DDI_PROP_SUCCESS)
29769860Sgdamore@opensolaris.org 		return (-1);	/* Can't find it! */
29779860Sgdamore@opensolaris.org 
29789860Sgdamore@opensolaris.org 	/*
29799860Sgdamore@opensolaris.org 	 * The return value from this routine specifies which port number
29809860Sgdamore@opensolaris.org 	 * we are.  The primary port is denoted port 0.  On a QUAD card we
29819860Sgdamore@opensolaris.org 	 * should return 1, 2, and 3 from this routine.  The return value
29829860Sgdamore@opensolaris.org 	 * is used to modify the ethernet address from the SROM data.
29839860Sgdamore@opensolaris.org 	 */
29849860Sgdamore@opensolaris.org 
29859860Sgdamore@opensolaris.org #ifdef BUG_4010796
29869860Sgdamore@opensolaris.org 	{
29879860Sgdamore@opensolaris.org 	/*
29889860Sgdamore@opensolaris.org 	 * For the present, we remember the device number of our primary
29899860Sgdamore@opensolaris.org 	 * sibling and hope we and our other siblings are consecutively
29909860Sgdamore@opensolaris.org 	 * numbered up from there.  In the future perhaps the bootstrap
29919860Sgdamore@opensolaris.org 	 * will pass us the necessary information telling us which physical
29929860Sgdamore@opensolaris.org 	 * port we really are.
29939860Sgdamore@opensolaris.org 	 */
29949860Sgdamore@opensolaris.org 	pci_regspec_t	*assignp;
29959860Sgdamore@opensolaris.org 	int		assign_len;
29969860Sgdamore@opensolaris.org 	int 		devnum;
29979860Sgdamore@opensolaris.org 	int		primary_devnum;
29989860Sgdamore@opensolaris.org 
29999860Sgdamore@opensolaris.org 	primary_devnum = ddi_getprop(DDI_DEV_T_ANY, devinfo, 0,
30009860Sgdamore@opensolaris.org 	    "DNET_DEVNUM", -1);
30019860Sgdamore@opensolaris.org 	if (primary_devnum == -1)
30029860Sgdamore@opensolaris.org 		return (1);	/* XXX NEEDSWORK -- We have no better idea */
30039860Sgdamore@opensolaris.org 
30049860Sgdamore@opensolaris.org 	if ((ddi_getlongprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
30059860Sgdamore@opensolaris.org 	    "assigned-addresses", (caddr_t)&assignp,
30069860Sgdamore@opensolaris.org 	    &assign_len)) != DDI_PROP_SUCCESS)
30079860Sgdamore@opensolaris.org 		return (1);	/* XXX NEEDSWORK -- We have no better idea */
30089860Sgdamore@opensolaris.org 
30099860Sgdamore@opensolaris.org 	devnum = PCI_REG_DEV_G(assignp->pci_phys_hi);
30109860Sgdamore@opensolaris.org 	kmem_free(assignp, assign_len);
30119860Sgdamore@opensolaris.org 	return (devnum - primary_devnum);
30129860Sgdamore@opensolaris.org 	}
30139860Sgdamore@opensolaris.org #else
30149860Sgdamore@opensolaris.org 	return (1);	/* XXX NEEDSWORK -- We have no better idea */
30159860Sgdamore@opensolaris.org #endif
30169860Sgdamore@opensolaris.org }
30179860Sgdamore@opensolaris.org 
30189860Sgdamore@opensolaris.org 
30199860Sgdamore@opensolaris.org #ifdef BUG_4010796
30209860Sgdamore@opensolaris.org static void
set_alternative_srom_image(dev_info_t * devinfo,uchar_t * vi,int len)30219860Sgdamore@opensolaris.org set_alternative_srom_image(dev_info_t *devinfo, uchar_t *vi, int len)
30229860Sgdamore@opensolaris.org {
30239860Sgdamore@opensolaris.org 	int 		proplen;
30249860Sgdamore@opensolaris.org 	pci_regspec_t	*assignp;
30259860Sgdamore@opensolaris.org 	int		assign_len;
30269860Sgdamore@opensolaris.org 	int 		devnum;
30279860Sgdamore@opensolaris.org 
30289860Sgdamore@opensolaris.org 	if (ddi_getproplen(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
30299860Sgdamore@opensolaris.org 	    "DNET_SROM", &proplen) == DDI_PROP_SUCCESS ||
30309860Sgdamore@opensolaris.org 	    ddi_getproplen(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
30319860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, "DNET_SROM", &proplen) == DDI_PROP_SUCCESS)
30329860Sgdamore@opensolaris.org 		return;		/* Already done! */
30339860Sgdamore@opensolaris.org 
30349860Sgdamore@opensolaris.org 	/* function return value ignored */
30359860Sgdamore@opensolaris.org 	(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE,
30369860Sgdamore@opensolaris.org 	    ddi_get_parent(devinfo), "DNET_SROM", (uchar_t *)vi, len);
30379860Sgdamore@opensolaris.org 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devinfo,
30389860Sgdamore@opensolaris.org 	    "DNET_HACK", "hack");
30399860Sgdamore@opensolaris.org 
30409860Sgdamore@opensolaris.org 	if ((ddi_getlongprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
30419860Sgdamore@opensolaris.org 	    "assigned-addresses", (caddr_t)&assignp,
30429860Sgdamore@opensolaris.org 	    &assign_len)) == DDI_PROP_SUCCESS) {
30439860Sgdamore@opensolaris.org 		devnum = PCI_REG_DEV_G(assignp->pci_phys_hi);
30449860Sgdamore@opensolaris.org 		kmem_free(assignp, assign_len);
30459860Sgdamore@opensolaris.org 		/* function return value ignored */
30469860Sgdamore@opensolaris.org 		(void) ddi_prop_update_int(DDI_DEV_T_NONE,
30479860Sgdamore@opensolaris.org 		    ddi_get_parent(devinfo), "DNET_DEVNUM", devnum);
30489860Sgdamore@opensolaris.org 	}
30499860Sgdamore@opensolaris.org }
30509860Sgdamore@opensolaris.org #endif
30519860Sgdamore@opensolaris.org 
30529860Sgdamore@opensolaris.org /*
30539860Sgdamore@opensolaris.org  *	========== SROM Parsing Routines ==========
30549860Sgdamore@opensolaris.org  */
30559860Sgdamore@opensolaris.org 
30569860Sgdamore@opensolaris.org static int
check_srom_valid(uchar_t * vi)30579860Sgdamore@opensolaris.org check_srom_valid(uchar_t *vi)
30589860Sgdamore@opensolaris.org {
30599860Sgdamore@opensolaris.org 	int		word, bit;
30609860Sgdamore@opensolaris.org 	uint8_t		crc;
30619860Sgdamore@opensolaris.org 	uint16_t	*wvi;		/* word16 pointer to vendor info */
30629860Sgdamore@opensolaris.org 	uint16_t	bitval;
30639860Sgdamore@opensolaris.org 
30649860Sgdamore@opensolaris.org 	/* verify that the number of controllers on the card is within range */
30659860Sgdamore@opensolaris.org 	if (vi[SROM_ADAPTER_CNT] < 1 || vi[SROM_ADAPTER_CNT] > MAX_ADAPTERS)
30669860Sgdamore@opensolaris.org 		return (0);
30679860Sgdamore@opensolaris.org 
30689860Sgdamore@opensolaris.org 	/*
30699860Sgdamore@opensolaris.org 	 * version 1 and 3 of this card did not check the id block CRC value
30709860Sgdamore@opensolaris.org 	 * and this can't be changed without retesting every supported card
30719860Sgdamore@opensolaris.org 	 *
30729860Sgdamore@opensolaris.org 	 * however version 4 of the SROM can have this test applied
30739860Sgdamore@opensolaris.org 	 * without fear of breaking something that used to work.
30749860Sgdamore@opensolaris.org 	 * the CRC algorithm is taken from the Intel document
30759860Sgdamore@opensolaris.org 	 *	"21x4 Serial ROM Format"
30769860Sgdamore@opensolaris.org 	 *	version 4.09
30779860Sgdamore@opensolaris.org 	 *	3-Mar-1999
30789860Sgdamore@opensolaris.org 	 */
30799860Sgdamore@opensolaris.org 
30809860Sgdamore@opensolaris.org 	switch (vi[SROM_VERSION]) {
30819860Sgdamore@opensolaris.org 	case 1:
30829860Sgdamore@opensolaris.org 	    /* fallthru */
30839860Sgdamore@opensolaris.org 	case 3:
30849860Sgdamore@opensolaris.org 		return (vi[SROM_MBZ] == 0 &&	/* must be zero */
30859860Sgdamore@opensolaris.org 		    vi[SROM_MBZ2] == 0 &&	/* must be zero */
30869860Sgdamore@opensolaris.org 		    vi[SROM_MBZ3] == 0);	/* must be zero */
30879860Sgdamore@opensolaris.org 
30889860Sgdamore@opensolaris.org 	case 4:
30899860Sgdamore@opensolaris.org 		wvi = (uint16_t *)vi;
30909860Sgdamore@opensolaris.org 		crc = 0xff;
30919860Sgdamore@opensolaris.org 		for (word = 0; word < 9; word++)
30929860Sgdamore@opensolaris.org 			for (bit = 15; bit >= 0; bit--) {
30939860Sgdamore@opensolaris.org 				if (word == 8 && bit == 7)
30949860Sgdamore@opensolaris.org 					return (crc == vi[16]);
30959860Sgdamore@opensolaris.org 				bitval =
30969860Sgdamore@opensolaris.org 				    ((wvi[word] >> bit) & 1) ^ ((crc >> 7) & 1);
30979860Sgdamore@opensolaris.org 				crc <<= 1;
30989860Sgdamore@opensolaris.org 				if (bitval == 1) {
30999860Sgdamore@opensolaris.org 					crc ^= 7;
31009860Sgdamore@opensolaris.org 				}
31019860Sgdamore@opensolaris.org 			}
31029860Sgdamore@opensolaris.org 
31039860Sgdamore@opensolaris.org 	default:
31049860Sgdamore@opensolaris.org 		return (0);
31059860Sgdamore@opensolaris.org 	}
31069860Sgdamore@opensolaris.org }
31079860Sgdamore@opensolaris.org 
31089860Sgdamore@opensolaris.org /*
31099860Sgdamore@opensolaris.org  *	========== Active Media Determination Routines ==========
31109860Sgdamore@opensolaris.org  */
31119860Sgdamore@opensolaris.org 
31129860Sgdamore@opensolaris.org /* This routine is also called for V3 Compact and extended type 0 SROMs */
31139860Sgdamore@opensolaris.org static int
is_fdmedia(int media)31149860Sgdamore@opensolaris.org is_fdmedia(int media)
31159860Sgdamore@opensolaris.org {
31169860Sgdamore@opensolaris.org 	if (media == MEDIA_TP_FD || media == MEDIA_SYM_SCR_FD)
31179860Sgdamore@opensolaris.org 		return (1);
31189860Sgdamore@opensolaris.org 	else
31199860Sgdamore@opensolaris.org 		return (0);
31209860Sgdamore@opensolaris.org }
31219860Sgdamore@opensolaris.org 
31229860Sgdamore@opensolaris.org /*
31239860Sgdamore@opensolaris.org  * "Linkset" is used to merge media that use the same link test check. So,
31249860Sgdamore@opensolaris.org  * if the TP link is added to the linkset, so is the TP Full duplex link.
31259860Sgdamore@opensolaris.org  * Used to avoid checking the same link status twice.
31269860Sgdamore@opensolaris.org  */
31279860Sgdamore@opensolaris.org static void
linkset_add(uint32_t * set,int media)31289860Sgdamore@opensolaris.org linkset_add(uint32_t *set, int media)
31299860Sgdamore@opensolaris.org {
31309860Sgdamore@opensolaris.org 	if (media == MEDIA_TP_FD || media == MEDIA_TP)
31319860Sgdamore@opensolaris.org 		*set |= (1UL<<MEDIA_TP_FD) | (1UL<<MEDIA_TP);
31329860Sgdamore@opensolaris.org 	else if (media == MEDIA_SYM_SCR_FD || media == MEDIA_SYM_SCR)
31339860Sgdamore@opensolaris.org 		*set |= (1UL<<MEDIA_SYM_SCR_FD) | (1UL<<MEDIA_SYM_SCR);
31349860Sgdamore@opensolaris.org 	else *set |= 1UL<<media;
31359860Sgdamore@opensolaris.org }
31369860Sgdamore@opensolaris.org static int
linkset_isset(uint32_t linkset,int media)31379860Sgdamore@opensolaris.org linkset_isset(uint32_t linkset, int media)
31389860Sgdamore@opensolaris.org {
31399860Sgdamore@opensolaris.org 	return (((1UL<<media)  & linkset) ? 1:0);
31409860Sgdamore@opensolaris.org }
31419860Sgdamore@opensolaris.org 
31429860Sgdamore@opensolaris.org /*
31439860Sgdamore@opensolaris.org  * The following code detects which Media is connected for 21041/21140
31449860Sgdamore@opensolaris.org  * Expect to change this code to support new 21140 variants.
31459860Sgdamore@opensolaris.org  * find_active_media() - called with intrlock held.
31469860Sgdamore@opensolaris.org  */
31479860Sgdamore@opensolaris.org static void
find_active_media(struct dnetinstance * dnetp)314810340Sstallion@opensolaris.org find_active_media(struct dnetinstance *dnetp)
31499860Sgdamore@opensolaris.org {
31509860Sgdamore@opensolaris.org 	int i;
31519860Sgdamore@opensolaris.org 	media_block_t *block;
31529860Sgdamore@opensolaris.org 	media_block_t *best_allowed = NULL;
31539860Sgdamore@opensolaris.org 	media_block_t *hd_found = NULL;
31549860Sgdamore@opensolaris.org 	media_block_t *fd_found = NULL;
31559860Sgdamore@opensolaris.org 	LEAF_FORMAT *leaf = &dnetp->sr.leaf[dnetp->leaf];
31569860Sgdamore@opensolaris.org 	uint32_t checked = 0, links_up = 0;
31579860Sgdamore@opensolaris.org 
31589860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
315910340Sstallion@opensolaris.org 
31609860Sgdamore@opensolaris.org 	dnetp->selected_media_block = leaf->default_block;
31619860Sgdamore@opensolaris.org 
31629860Sgdamore@opensolaris.org 	if (dnetp->phyaddr != -1) {
31639860Sgdamore@opensolaris.org 		dnetp->selected_media_block = leaf->mii_block;
316410340Sstallion@opensolaris.org 		setup_block(dnetp);
31659860Sgdamore@opensolaris.org 
31669860Sgdamore@opensolaris.org 		if (ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
31679860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, "portmon", 1)) {
31689860Sgdamore@opensolaris.org 			/* XXX return value ignored */
31699860Sgdamore@opensolaris.org 			(void) mii_start_portmon(dnetp->mii, dnet_mii_link_cb,
31709860Sgdamore@opensolaris.org 			    &dnetp->intrlock);
31719860Sgdamore@opensolaris.org 			/*
31729860Sgdamore@opensolaris.org 			 * If the port monitor detects the link is already
31739860Sgdamore@opensolaris.org 			 * up, there is no point going through the rest of the
31749860Sgdamore@opensolaris.org 			 * link sense
31759860Sgdamore@opensolaris.org 			 */
31769860Sgdamore@opensolaris.org 			if (dnetp->mii_up) {
31779860Sgdamore@opensolaris.org 				return;
31789860Sgdamore@opensolaris.org 			}
31799860Sgdamore@opensolaris.org 		}
31809860Sgdamore@opensolaris.org 	}
31819860Sgdamore@opensolaris.org 
31829860Sgdamore@opensolaris.org 	/*
31839860Sgdamore@opensolaris.org 	 * Media is searched for in order of Precedence. This DEC SROM spec
31849860Sgdamore@opensolaris.org 	 * tells us that the first media entry in the SROM is the lowest
31859860Sgdamore@opensolaris.org 	 * precedence and should be checked last. This is why we go to the last
31869860Sgdamore@opensolaris.org 	 * Media block and work back to the beginning.
31879860Sgdamore@opensolaris.org 	 *
31889860Sgdamore@opensolaris.org 	 * However, some older SROMs (Cogent EM110's etc.) have this the wrong
31899860Sgdamore@opensolaris.org 	 * way around. As a result, following the SROM spec would result in a
31909860Sgdamore@opensolaris.org 	 * 10 link being chosen over a 100 link if both media are available.
31919860Sgdamore@opensolaris.org 	 * So we continue trying the media until we have at least tried the
31929860Sgdamore@opensolaris.org 	 * DEFAULT media.
31939860Sgdamore@opensolaris.org 	 */
31949860Sgdamore@opensolaris.org 
31959860Sgdamore@opensolaris.org 	/* Search for an active medium, and select it */
31969860Sgdamore@opensolaris.org 	for (block = leaf->block + leaf->block_count  - 1;
31979860Sgdamore@opensolaris.org 	    block >= leaf->block; block--) {
31989860Sgdamore@opensolaris.org 		int media = block->media_code;
31999860Sgdamore@opensolaris.org 
32009860Sgdamore@opensolaris.org 		/* User settings disallow selection of this block */
32019860Sgdamore@opensolaris.org 		if (dnetp->disallowed_media & (1UL<<media))
32029860Sgdamore@opensolaris.org 			continue;
32039860Sgdamore@opensolaris.org 
32049860Sgdamore@opensolaris.org 		/* We may not be able to pick the default */
32059860Sgdamore@opensolaris.org 		if (best_allowed == NULL || block == leaf->default_block)
32069860Sgdamore@opensolaris.org 			best_allowed = block;
32079860Sgdamore@opensolaris.org #ifdef DEBUG
32089860Sgdamore@opensolaris.org 		if (dnetdebug & DNETSENSE)
32099860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "Testing %s medium (block type %d)",
32109860Sgdamore@opensolaris.org 			    media_str[media], block->type);
32119860Sgdamore@opensolaris.org #endif
32129860Sgdamore@opensolaris.org 
32139860Sgdamore@opensolaris.org 		dnetp->selected_media_block = block;
32149860Sgdamore@opensolaris.org 		switch (block->type) {
32159860Sgdamore@opensolaris.org 
32169860Sgdamore@opensolaris.org 		case 2: /* SIA Media block: Best we can do is send a packet */
321710340Sstallion@opensolaris.org 			setup_block(dnetp);
321810340Sstallion@opensolaris.org 			if (send_test_packet(dnetp)) {
32199860Sgdamore@opensolaris.org 				if (!is_fdmedia(media))
32209860Sgdamore@opensolaris.org 					return;
32219860Sgdamore@opensolaris.org 				if (!fd_found)
32229860Sgdamore@opensolaris.org 					fd_found = block;
32239860Sgdamore@opensolaris.org 			}
32249860Sgdamore@opensolaris.org 			break;
32259860Sgdamore@opensolaris.org 
32269860Sgdamore@opensolaris.org 		/* SYM/SCR or TP block: Use the link-sense bits */
32279860Sgdamore@opensolaris.org 		case 0:
32289860Sgdamore@opensolaris.org 			if (!linkset_isset(checked, media)) {
32299860Sgdamore@opensolaris.org 				linkset_add(&checked, media);
32309860Sgdamore@opensolaris.org 				if (((media == MEDIA_BNC ||
32319860Sgdamore@opensolaris.org 				    media == MEDIA_AUI) &&
323210340Sstallion@opensolaris.org 				    send_test_packet(dnetp)) ||
323310340Sstallion@opensolaris.org 				    dnet_link_sense(dnetp))
32349860Sgdamore@opensolaris.org 					linkset_add(&links_up, media);
32359860Sgdamore@opensolaris.org 			}
32369860Sgdamore@opensolaris.org 
32379860Sgdamore@opensolaris.org 			if (linkset_isset(links_up, media)) {
32389860Sgdamore@opensolaris.org 				/*
32399860Sgdamore@opensolaris.org 				 * Half Duplex is *always* the favoured media.
32409860Sgdamore@opensolaris.org 				 * Full Duplex can be set and forced via the
32419860Sgdamore@opensolaris.org 				 * conf file.
32429860Sgdamore@opensolaris.org 				 */
32439860Sgdamore@opensolaris.org 				if (!is_fdmedia(media) &&
32449860Sgdamore@opensolaris.org 				    dnetp->selected_media_block ==
32459860Sgdamore@opensolaris.org 				    leaf->default_block) {
32469860Sgdamore@opensolaris.org 					/*
32479860Sgdamore@opensolaris.org 					 * Cogent cards have the media in
32489860Sgdamore@opensolaris.org 					 * opposite order to the spec.,
32499860Sgdamore@opensolaris.org 					 * this code forces the media test to
32509860Sgdamore@opensolaris.org 					 * keep going until the default media
32519860Sgdamore@opensolaris.org 					 * is tested.
32529860Sgdamore@opensolaris.org 					 *
32539860Sgdamore@opensolaris.org 					 * In Cogent case, 10, 10FD, 100FD, 100
32549860Sgdamore@opensolaris.org 					 * 100 is the default but 10 could have
32559860Sgdamore@opensolaris.org 					 * been detected and would have been
32569860Sgdamore@opensolaris.org 					 * chosen but now we force it through to
32579860Sgdamore@opensolaris.org 					 * 100.
32589860Sgdamore@opensolaris.org 					 */
325910340Sstallion@opensolaris.org 					setup_block(dnetp);
32609860Sgdamore@opensolaris.org 					return;
32619860Sgdamore@opensolaris.org 				} else if (!is_fdmedia(media)) {
32629860Sgdamore@opensolaris.org 					/*
32639860Sgdamore@opensolaris.org 					 * This allows all the others to work
32649860Sgdamore@opensolaris.org 					 * properly by remembering the media
32659860Sgdamore@opensolaris.org 					 * that works and not defaulting to
32669860Sgdamore@opensolaris.org 					 * a FD link.
32679860Sgdamore@opensolaris.org 					 */
32689860Sgdamore@opensolaris.org 						if (hd_found == NULL)
32699860Sgdamore@opensolaris.org 							hd_found = block;
32709860Sgdamore@opensolaris.org 				} else if (fd_found == NULL) {
32719860Sgdamore@opensolaris.org 					/*
32729860Sgdamore@opensolaris.org 					 * No media have already been found
32739860Sgdamore@opensolaris.org 					 * so far, this is FD, it works so
32749860Sgdamore@opensolaris.org 					 * remember it and if no others are
32759860Sgdamore@opensolaris.org 					 * detected, use it.
32769860Sgdamore@opensolaris.org 					 */
32779860Sgdamore@opensolaris.org 					fd_found = block;
32789860Sgdamore@opensolaris.org 				}
32799860Sgdamore@opensolaris.org 			}
32809860Sgdamore@opensolaris.org 			break;
32819860Sgdamore@opensolaris.org 
32829860Sgdamore@opensolaris.org 		/*
32839860Sgdamore@opensolaris.org 		 * MII block: May take up to a second or so to settle if
32849860Sgdamore@opensolaris.org 		 * setup causes a PHY reset
32859860Sgdamore@opensolaris.org 		 */
32869860Sgdamore@opensolaris.org 		case 1: case 3:
328710340Sstallion@opensolaris.org 			setup_block(dnetp);
32889860Sgdamore@opensolaris.org 			for (i = 0; ; i++) {
32899860Sgdamore@opensolaris.org 				if (mii_linkup(dnetp->mii, dnetp->phyaddr)) {
32909860Sgdamore@opensolaris.org 					/* XXX function return value ignored */
32919860Sgdamore@opensolaris.org 					(void) mii_getspeed(dnetp->mii,
32929860Sgdamore@opensolaris.org 					    dnetp->phyaddr,
32939860Sgdamore@opensolaris.org 					    &dnetp->mii_speed,
32949860Sgdamore@opensolaris.org 					    &dnetp->mii_duplex);
32959860Sgdamore@opensolaris.org 					dnetp->mii_up = 1;
32969860Sgdamore@opensolaris.org 					leaf->mii_block = block;
32979860Sgdamore@opensolaris.org 					return;
32989860Sgdamore@opensolaris.org 				}
32999860Sgdamore@opensolaris.org 				if (i == 10)
33009860Sgdamore@opensolaris.org 					break;
33019860Sgdamore@opensolaris.org 				delay(drv_usectohz(150000));
33029860Sgdamore@opensolaris.org 			}
33039860Sgdamore@opensolaris.org 			dnetp->mii_up = 0;
33049860Sgdamore@opensolaris.org 			break;
33059860Sgdamore@opensolaris.org 		}
33069860Sgdamore@opensolaris.org 	} /* for loop */
33079860Sgdamore@opensolaris.org 	if (hd_found) {
33089860Sgdamore@opensolaris.org 		dnetp->selected_media_block = hd_found;
33099860Sgdamore@opensolaris.org 	} else if (fd_found) {
33109860Sgdamore@opensolaris.org 		dnetp->selected_media_block = fd_found;
33119860Sgdamore@opensolaris.org 	} else {
33129860Sgdamore@opensolaris.org 		if (best_allowed == NULL)
33139860Sgdamore@opensolaris.org 			best_allowed = leaf->default_block;
33149860Sgdamore@opensolaris.org 		dnetp->selected_media_block = best_allowed;
33159860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "!dnet: Default media selected\n");
33169860Sgdamore@opensolaris.org 	}
331710340Sstallion@opensolaris.org 	setup_block(dnetp);
33189860Sgdamore@opensolaris.org }
33199860Sgdamore@opensolaris.org 
33209860Sgdamore@opensolaris.org /*
33219860Sgdamore@opensolaris.org  * Do anything neccessary to select the selected_media_block.
33229860Sgdamore@opensolaris.org  * setup_block() - called with intrlock held.
33239860Sgdamore@opensolaris.org  */
33249860Sgdamore@opensolaris.org static void
setup_block(struct dnetinstance * dnetp)332510340Sstallion@opensolaris.org setup_block(struct dnetinstance *dnetp)
33269860Sgdamore@opensolaris.org {
332710340Sstallion@opensolaris.org 	dnet_reset_board(dnetp);
332810340Sstallion@opensolaris.org 	dnet_init_board(dnetp);
33299860Sgdamore@opensolaris.org 	/* XXX function return value ignored */
333010340Sstallion@opensolaris.org 	(void) dnet_start(dnetp);
33319860Sgdamore@opensolaris.org }
33329860Sgdamore@opensolaris.org 
33339860Sgdamore@opensolaris.org /* dnet_link_sense() - called with intrlock held */
33349860Sgdamore@opensolaris.org static int
dnet_link_sense(struct dnetinstance * dnetp)333510340Sstallion@opensolaris.org dnet_link_sense(struct dnetinstance *dnetp)
33369860Sgdamore@opensolaris.org {
33379860Sgdamore@opensolaris.org 	/*
33389860Sgdamore@opensolaris.org 	 * This routine makes use of the command word from the srom config.
33399860Sgdamore@opensolaris.org 	 * Details of the auto-sensing information contained in this can
33409860Sgdamore@opensolaris.org 	 * be found in the "Digital Semiconductor 21X4 Serial ROM Format v3.03"
33419860Sgdamore@opensolaris.org 	 * spec. Section 4.3.2.1, and 4.5.2.1.3
33429860Sgdamore@opensolaris.org 	 */
33439860Sgdamore@opensolaris.org 	media_block_t *block = dnetp->selected_media_block;
33449860Sgdamore@opensolaris.org 	uint32_t link, status, mask, polarity;
33459860Sgdamore@opensolaris.org 	int settletime, stabletime, waittime, upsamples;
33469860Sgdamore@opensolaris.org 	int delay_100, delay_10;
33479860Sgdamore@opensolaris.org 
33489860Sgdamore@opensolaris.org 
33499860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
33509860Sgdamore@opensolaris.org 	/* Don't autosense if the medium does not support it */
33519860Sgdamore@opensolaris.org 	if (block->command & (1 << 15)) {
33529860Sgdamore@opensolaris.org 		/* This should be the default block */
33539860Sgdamore@opensolaris.org 		if (block->command & (1UL<<14))
33549860Sgdamore@opensolaris.org 			dnetp->sr.leaf[dnetp->leaf].default_block = block;
33559860Sgdamore@opensolaris.org 		return (0);
33569860Sgdamore@opensolaris.org 	}
33579860Sgdamore@opensolaris.org 
33589860Sgdamore@opensolaris.org 	delay_100 = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
33599860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, "autosense-delay-100", 2000);
33609860Sgdamore@opensolaris.org 
33619860Sgdamore@opensolaris.org 	delay_10 = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
33629860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, "autosense-delay-10", 400);
33639860Sgdamore@opensolaris.org 
33649860Sgdamore@opensolaris.org 	/*
33659860Sgdamore@opensolaris.org 	 * Scrambler may need to be disabled for link sensing
33669860Sgdamore@opensolaris.org 	 * to work
33679860Sgdamore@opensolaris.org 	 */
33689860Sgdamore@opensolaris.org 	dnetp->disable_scrambler = 1;
336910340Sstallion@opensolaris.org 	setup_block(dnetp);
33709860Sgdamore@opensolaris.org 	dnetp->disable_scrambler = 0;
33719860Sgdamore@opensolaris.org 
33729860Sgdamore@opensolaris.org 	if (block->media_code == MEDIA_TP || block->media_code == MEDIA_TP_FD)
33739860Sgdamore@opensolaris.org 		settletime = delay_10;
33749860Sgdamore@opensolaris.org 	else
33759860Sgdamore@opensolaris.org 		settletime = delay_100;
33769860Sgdamore@opensolaris.org 	stabletime = settletime / 4;
33779860Sgdamore@opensolaris.org 
33789860Sgdamore@opensolaris.org 	mask = 1 << ((block->command & CMD_MEDIABIT_MASK) >> 1);
33799860Sgdamore@opensolaris.org 	polarity = block->command & CMD_POL ? 0xffffffff : 0;
33809860Sgdamore@opensolaris.org 
33819860Sgdamore@opensolaris.org 	for (waittime = 0, upsamples = 0;
33829860Sgdamore@opensolaris.org 	    waittime <= settletime + stabletime && upsamples < 8;
33839860Sgdamore@opensolaris.org 	    waittime += stabletime/8) {
33849860Sgdamore@opensolaris.org 		delay(drv_usectohz(stabletime*1000 / 8));
33859860Sgdamore@opensolaris.org 		status = read_gpr(dnetp);
33869860Sgdamore@opensolaris.org 		link = (status^polarity) & mask;
33879860Sgdamore@opensolaris.org 		if (link)
33889860Sgdamore@opensolaris.org 			upsamples++;
33899860Sgdamore@opensolaris.org 		else
33909860Sgdamore@opensolaris.org 			upsamples = 0;
33919860Sgdamore@opensolaris.org 	}
33929860Sgdamore@opensolaris.org #ifdef DNETDEBUG
33939860Sgdamore@opensolaris.org 	if (dnetdebug & DNETSENSE)
33949860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "%s upsamples:%d stat:%x polarity:%x "
33959860Sgdamore@opensolaris.org 		    "mask:%x link:%x",
33969860Sgdamore@opensolaris.org 		    upsamples == 8 ? "UP":"DOWN",
33979860Sgdamore@opensolaris.org 		    upsamples, status, polarity, mask, link);
33989860Sgdamore@opensolaris.org #endif
33999860Sgdamore@opensolaris.org 	if (upsamples == 8)
34009860Sgdamore@opensolaris.org 		return (1);
34019860Sgdamore@opensolaris.org 	return (0);
34029860Sgdamore@opensolaris.org }
34039860Sgdamore@opensolaris.org 
34049860Sgdamore@opensolaris.org static int
send_test_packet(struct dnetinstance * dnetp)340510340Sstallion@opensolaris.org send_test_packet(struct dnetinstance *dnetp)
34069860Sgdamore@opensolaris.org {
34079860Sgdamore@opensolaris.org 	int packet_delay;
34089860Sgdamore@opensolaris.org 	struct tx_desc_type *desc;
34099860Sgdamore@opensolaris.org 	int bufindex;
34109860Sgdamore@opensolaris.org 	int media_code = dnetp->selected_media_block->media_code;
34119860Sgdamore@opensolaris.org 	uint32_t del;
34129860Sgdamore@opensolaris.org 
34139860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
34149860Sgdamore@opensolaris.org 	/*
34159860Sgdamore@opensolaris.org 	 * For a successful test packet, the card must have settled into
34169860Sgdamore@opensolaris.org 	 * its current setting.  Almost all cards we've tested manage to
34179860Sgdamore@opensolaris.org 	 * do this with all media within 50ms.  However, the SMC 8432
34189860Sgdamore@opensolaris.org 	 * requires 300ms to settle into BNC mode.  We now only do this
34199860Sgdamore@opensolaris.org 	 * from attach, and we do sleeping delay() instead of drv_usecwait()
34209860Sgdamore@opensolaris.org 	 * so we hope this .2 second delay won't cause too much suffering.
34219860Sgdamore@opensolaris.org 	 * ALSO: with an autonegotiating hub, an aditional 1 second delay is
34229860Sgdamore@opensolaris.org 	 * required. This is done if the media type is TP
34239860Sgdamore@opensolaris.org 	 */
34249860Sgdamore@opensolaris.org 	if (media_code == MEDIA_TP || media_code == MEDIA_TP_FD) {
34259860Sgdamore@opensolaris.org 		packet_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
34269860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, "test_packet_delay_tp", 1300000);
34279860Sgdamore@opensolaris.org 	} else {
34289860Sgdamore@opensolaris.org 		packet_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
34299860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, "test_packet_delay", 300000);
34309860Sgdamore@opensolaris.org 	}
34319860Sgdamore@opensolaris.org 	delay(drv_usectohz(packet_delay));
34329860Sgdamore@opensolaris.org 
34339860Sgdamore@opensolaris.org 	desc = dnetp->tx_desc;
34349860Sgdamore@opensolaris.org 
34359860Sgdamore@opensolaris.org 	bufindex = dnetp->tx_current_desc;
343610340Sstallion@opensolaris.org 	if (alloc_descriptor(dnetp) == FAILURE) {
34379860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "DNET: send_test_packet: alloc_descriptor"
34389860Sgdamore@opensolaris.org 		    "failed");
34399860Sgdamore@opensolaris.org 		return (0);
34409860Sgdamore@opensolaris.org 	}
34419860Sgdamore@opensolaris.org 
34429860Sgdamore@opensolaris.org 	/*
34439860Sgdamore@opensolaris.org 	 * use setup buffer as the buffer for the test packet
34449860Sgdamore@opensolaris.org 	 * instead of allocating one.
34459860Sgdamore@opensolaris.org 	 */
34469860Sgdamore@opensolaris.org 
34479860Sgdamore@opensolaris.org 	ASSERT(dnetp->setup_buf_vaddr != NULL);
34489860Sgdamore@opensolaris.org 	/* Put something decent in dest address so we don't annoy other cards */
34499860Sgdamore@opensolaris.org 	BCOPY((caddr_t)dnetp->curr_macaddr,
34509860Sgdamore@opensolaris.org 	    (caddr_t)dnetp->setup_buf_vaddr, ETHERADDRL);
34519860Sgdamore@opensolaris.org 	BCOPY((caddr_t)dnetp->curr_macaddr,
34529860Sgdamore@opensolaris.org 	    (caddr_t)dnetp->setup_buf_vaddr+ETHERADDRL, ETHERADDRL);
34539860Sgdamore@opensolaris.org 
34549860Sgdamore@opensolaris.org 	desc[bufindex].buffer1 = dnetp->setup_buf_paddr;
34559860Sgdamore@opensolaris.org 	desc[bufindex].desc1.buffer_size1 = SETUPBUF_SIZE;
34569860Sgdamore@opensolaris.org 	desc[bufindex].buffer2 = (uint32_t)(0);
34579860Sgdamore@opensolaris.org 	desc[bufindex].desc1.first_desc = 1;
34589860Sgdamore@opensolaris.org 	desc[bufindex].desc1.last_desc = 1;
34599860Sgdamore@opensolaris.org 	desc[bufindex].desc1.int_on_comp = 1;
34609860Sgdamore@opensolaris.org 	desc[bufindex].desc0.own = 1;
34619860Sgdamore@opensolaris.org 
34629860Sgdamore@opensolaris.org 	ddi_put8(dnetp->io_handle, REG8(dnetp->io_reg, TX_POLL_REG),
34639860Sgdamore@opensolaris.org 	    TX_POLL_DEMAND);
34649860Sgdamore@opensolaris.org 
34659860Sgdamore@opensolaris.org 	/*
34669860Sgdamore@opensolaris.org 	 * Give enough time for the chip to transmit the packet
34679860Sgdamore@opensolaris.org 	 */
34689860Sgdamore@opensolaris.org #if 1
34699860Sgdamore@opensolaris.org 	del = 1000;
34709860Sgdamore@opensolaris.org 	while (desc[bufindex].desc0.own && --del)
34719860Sgdamore@opensolaris.org 		drv_usecwait(10);	/* quickly wait up to 10ms */
34729860Sgdamore@opensolaris.org 	if (desc[bufindex].desc0.own)
34739860Sgdamore@opensolaris.org 		delay(drv_usectohz(200000));	/* nicely wait a longer time */
34749860Sgdamore@opensolaris.org #else
34759860Sgdamore@opensolaris.org 	del = 0x10000;
34769860Sgdamore@opensolaris.org 	while (desc[bufindex].desc0.own && --del)
34779860Sgdamore@opensolaris.org 		drv_usecwait(10);
34789860Sgdamore@opensolaris.org #endif
34799860Sgdamore@opensolaris.org 
34809860Sgdamore@opensolaris.org #ifdef DNETDEBUG
34819860Sgdamore@opensolaris.org 	if (dnetdebug & DNETSENSE)
34829860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "desc0 bits = %u, %u, %u, %u, %u, %u",
34839860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.own,
34849860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.err_summary,
34859860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.carrier_loss,
34869860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.no_carrier,
34879860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.late_collision,
34889860Sgdamore@opensolaris.org 		    desc[bufindex].desc0.link_fail);
34899860Sgdamore@opensolaris.org #endif
34909860Sgdamore@opensolaris.org 	if (desc[bufindex].desc0.own) /* it shouldn't take this long, error */
34919860Sgdamore@opensolaris.org 		return (0);
34929860Sgdamore@opensolaris.org 
34939860Sgdamore@opensolaris.org 	return (!desc[bufindex].desc0.err_summary);
34949860Sgdamore@opensolaris.org }
34959860Sgdamore@opensolaris.org 
34969860Sgdamore@opensolaris.org /* enable_interrupts - called with intrlock held */
34979860Sgdamore@opensolaris.org static void
enable_interrupts(struct dnetinstance * dnetp)349810340Sstallion@opensolaris.org enable_interrupts(struct dnetinstance *dnetp)
34999860Sgdamore@opensolaris.org {
35009860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
35019860Sgdamore@opensolaris.org 	/* Don't enable interrupts if they have been forced off */
35029860Sgdamore@opensolaris.org 	if (dnetp->interrupts_disabled)
35039860Sgdamore@opensolaris.org 		return;
35049860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, INT_MASK_REG),
350510340Sstallion@opensolaris.org 	    ABNORMAL_INTR_MASK | NORMAL_INTR_MASK | SYSTEM_ERROR_MASK |
35069860Sgdamore@opensolaris.org 	    (dnetp->timer.cb ? GPTIMER_INTR : 0) |
350710340Sstallion@opensolaris.org 	    RX_INTERRUPT_MASK |
350810340Sstallion@opensolaris.org 	    TX_INTERRUPT_MASK | TX_JABBER_MASK | TX_UNDERFLOW_MASK);
35099860Sgdamore@opensolaris.org }
35109860Sgdamore@opensolaris.org 
35119860Sgdamore@opensolaris.org /*
35129860Sgdamore@opensolaris.org  * Some older multiport cards are non-PCI compliant in their interrupt routing.
35139860Sgdamore@opensolaris.org  * Second and subsequent devices are incorrectly configured by the BIOS
35149860Sgdamore@opensolaris.org  * (either in their ILINE configuration or the MP Configuration Table for PC+MP
35159860Sgdamore@opensolaris.org  * systems).
351610340Sstallion@opensolaris.org  * The hack stops registering the interrupt routine for the FIRST
351710340Sstallion@opensolaris.org  * device on the adapter, and registers its own. It builds up a table
351810340Sstallion@opensolaris.org  * of dnetp structures for each device, and the new interrupt routine
351910340Sstallion@opensolaris.org  * calls dnet_intr for each of them.
35209860Sgdamore@opensolaris.org  * Known cards that suffer from this problem are:
35219860Sgdamore@opensolaris.org  *	All Cogent multiport cards;
35229860Sgdamore@opensolaris.org  * 	Znyx 314;
35239860Sgdamore@opensolaris.org  *	Znyx 315.
35249860Sgdamore@opensolaris.org  *
35259860Sgdamore@opensolaris.org  * XXX NEEDSWORK -- see comments above get_alternative_srom_image(). This
35269860Sgdamore@opensolaris.org  * hack relies on the fact that the offending cards will have only one SROM.
35279860Sgdamore@opensolaris.org  * It uses this fact to identify devices that are on the same multiport
35289860Sgdamore@opensolaris.org  * adapter, as opposed to multiple devices from the same vendor (as
35299860Sgdamore@opensolaris.org  * indicated by "secondary")
35309860Sgdamore@opensolaris.org  */
35319860Sgdamore@opensolaris.org static int
dnet_hack_interrupts(struct dnetinstance * dnetp,int secondary)353210340Sstallion@opensolaris.org dnet_hack_interrupts(struct dnetinstance *dnetp, int secondary)
35339860Sgdamore@opensolaris.org {
35349860Sgdamore@opensolaris.org 	int i;
35359860Sgdamore@opensolaris.org 	struct hackintr_inf *hackintr_inf;
35369860Sgdamore@opensolaris.org 	dev_info_t *devinfo = dnetp->devinfo;
35379860Sgdamore@opensolaris.org 	uint32_t oui = 0;	/* Organizationally Unique ID */
35389860Sgdamore@opensolaris.org 
35399860Sgdamore@opensolaris.org 	if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
35409860Sgdamore@opensolaris.org 	    "no_INTA_workaround", 0) != 0)
35419860Sgdamore@opensolaris.org 		return (0);
35429860Sgdamore@opensolaris.org 
35439860Sgdamore@opensolaris.org 	for (i = 0; i < 3; i++)
35449860Sgdamore@opensolaris.org 		oui = (oui << 8) | dnetp->vendor_addr[i];
35459860Sgdamore@opensolaris.org 
35469860Sgdamore@opensolaris.org 	/* Check wheather or not we need to implement the hack */
35479860Sgdamore@opensolaris.org 
35489860Sgdamore@opensolaris.org 	switch (oui) {
35499860Sgdamore@opensolaris.org 	case ZNYX_ETHER:
35509860Sgdamore@opensolaris.org 		/* Znyx multiport 21040 cards <<==>> ZX314 or ZX315 */
35519860Sgdamore@opensolaris.org 		if (dnetp->board_type != DEVICE_ID_21040)
35529860Sgdamore@opensolaris.org 			return (0);
35539860Sgdamore@opensolaris.org 		break;
35549860Sgdamore@opensolaris.org 
35559860Sgdamore@opensolaris.org 	case COGENT_ETHER:
35569860Sgdamore@opensolaris.org 		/* All known Cogent multiport cards */
35579860Sgdamore@opensolaris.org 		break;
35589860Sgdamore@opensolaris.org 
35599860Sgdamore@opensolaris.org 	case ADAPTEC_ETHER:
35609860Sgdamore@opensolaris.org 		/* Adaptec multiport cards */
35619860Sgdamore@opensolaris.org 		break;
35629860Sgdamore@opensolaris.org 
35639860Sgdamore@opensolaris.org 	default:
35649860Sgdamore@opensolaris.org 		/* Other cards work correctly */
35659860Sgdamore@opensolaris.org 		return (0);
35669860Sgdamore@opensolaris.org 	}
35679860Sgdamore@opensolaris.org 
35689860Sgdamore@opensolaris.org 	/* card is (probably) non-PCI compliant in its interrupt routing */
35699860Sgdamore@opensolaris.org 
35709860Sgdamore@opensolaris.org 
35719860Sgdamore@opensolaris.org 	if (!secondary) {
35729860Sgdamore@opensolaris.org 
35739860Sgdamore@opensolaris.org 		/*
35749860Sgdamore@opensolaris.org 		 * If we have already registered a hacked interrupt, and
35759860Sgdamore@opensolaris.org 		 * this is also a 'primary' adapter, then this is NOT part of
35769860Sgdamore@opensolaris.org 		 * a multiport card, but a second card on the same PCI bus.
35779860Sgdamore@opensolaris.org 		 * BUGID: 4057747
35789860Sgdamore@opensolaris.org 		 */
35799860Sgdamore@opensolaris.org 		if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
35809860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, hackintr_propname, 0) != 0)
35819860Sgdamore@opensolaris.org 			return (0);
35829860Sgdamore@opensolaris.org 				/* ... Primary not part of a multiport device */
35839860Sgdamore@opensolaris.org 
35849860Sgdamore@opensolaris.org #ifdef DNETDEBUG
35859860Sgdamore@opensolaris.org 		if (dnetdebug & DNETTRACE)
35869860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "dnet: Implementing hardware "
35879860Sgdamore@opensolaris.org 			    "interrupt flaw workaround");
35889860Sgdamore@opensolaris.org #endif
35899860Sgdamore@opensolaris.org 		dnetp->hackintr_inf = hackintr_inf =
35909860Sgdamore@opensolaris.org 		    kmem_zalloc(sizeof (struct hackintr_inf), KM_SLEEP);
35919860Sgdamore@opensolaris.org 		if (hackintr_inf == NULL)
35929860Sgdamore@opensolaris.org 			goto fail;
35939860Sgdamore@opensolaris.org 
359410340Sstallion@opensolaris.org 		hackintr_inf->dnetps[0] = dnetp;
35959860Sgdamore@opensolaris.org 		hackintr_inf->devinfo = devinfo;
35969860Sgdamore@opensolaris.org 
35979860Sgdamore@opensolaris.org 		/*
35989860Sgdamore@opensolaris.org 		 * Add a property to allow successive attaches to find the
35999860Sgdamore@opensolaris.org 		 * table
36009860Sgdamore@opensolaris.org 		 */
36019860Sgdamore@opensolaris.org 
36029860Sgdamore@opensolaris.org 		if (ddi_prop_update_byte_array(DDI_DEV_T_NONE,
36039860Sgdamore@opensolaris.org 		    ddi_get_parent(devinfo), hackintr_propname,
36049860Sgdamore@opensolaris.org 		    (uchar_t *)&dnetp->hackintr_inf,
36059860Sgdamore@opensolaris.org 		    sizeof (void *)) != DDI_PROP_SUCCESS)
36069860Sgdamore@opensolaris.org 			goto fail;
36079860Sgdamore@opensolaris.org 
36089860Sgdamore@opensolaris.org 
36099860Sgdamore@opensolaris.org 		/* Register our hacked interrupt routine */
361010340Sstallion@opensolaris.org 		if (ddi_add_intr(devinfo, 0, &dnetp->icookie, NULL,
36119860Sgdamore@opensolaris.org 		    (uint_t (*)(char *))dnet_hack_intr,
36129860Sgdamore@opensolaris.org 		    (caddr_t)hackintr_inf) != DDI_SUCCESS) {
36139860Sgdamore@opensolaris.org 			/* XXX function return value ignored */
36149860Sgdamore@opensolaris.org 			(void) ddi_prop_remove(DDI_DEV_T_NONE,
36159860Sgdamore@opensolaris.org 			    ddi_get_parent(devinfo),
36169860Sgdamore@opensolaris.org 			    hackintr_propname);
36179860Sgdamore@opensolaris.org 			goto fail;
36189860Sgdamore@opensolaris.org 		}
36199860Sgdamore@opensolaris.org 
36209860Sgdamore@opensolaris.org 		/*
36219860Sgdamore@opensolaris.org 		 * Mutex required to ensure interrupt routine has completed
36229860Sgdamore@opensolaris.org 		 * when detaching devices
36239860Sgdamore@opensolaris.org 		 */
36249860Sgdamore@opensolaris.org 		mutex_init(&hackintr_inf->lock, NULL, MUTEX_DRIVER,
362510340Sstallion@opensolaris.org 		    dnetp->icookie);
36269860Sgdamore@opensolaris.org 
36279860Sgdamore@opensolaris.org 		/* Stop GLD registering an interrupt */
36289860Sgdamore@opensolaris.org 		return (-1);
36299860Sgdamore@opensolaris.org 	} else {
36309860Sgdamore@opensolaris.org 
363110340Sstallion@opensolaris.org 		/* Add the dnetp for this secondary device to the table */
36329860Sgdamore@opensolaris.org 
36339860Sgdamore@opensolaris.org 		hackintr_inf = (struct hackintr_inf *)(uintptr_t)
36349860Sgdamore@opensolaris.org 		    ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
36359860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, hackintr_propname, 0);
36369860Sgdamore@opensolaris.org 
36379860Sgdamore@opensolaris.org 		if (hackintr_inf == NULL)
36389860Sgdamore@opensolaris.org 			goto fail;
36399860Sgdamore@opensolaris.org 
36409860Sgdamore@opensolaris.org 		/* Find an empty slot */
36419860Sgdamore@opensolaris.org 		for (i = 0; i < MAX_INST; i++)
364210340Sstallion@opensolaris.org 			if (hackintr_inf->dnetps[i] == NULL)
36439860Sgdamore@opensolaris.org 				break;
36449860Sgdamore@opensolaris.org 
36459860Sgdamore@opensolaris.org 		/* More than 8 ports on adapter ?! */
36469860Sgdamore@opensolaris.org 		if (i == MAX_INST)
36479860Sgdamore@opensolaris.org 			goto fail;
36489860Sgdamore@opensolaris.org 
364910340Sstallion@opensolaris.org 		hackintr_inf->dnetps[i] = dnetp;
36509860Sgdamore@opensolaris.org 
36519860Sgdamore@opensolaris.org 		/*
36529860Sgdamore@opensolaris.org 		 * Allow GLD to register a handler for this
36539860Sgdamore@opensolaris.org 		 * device. If the card is actually broken, as we suspect, this
36549860Sgdamore@opensolaris.org 		 * handler will never get called. However, by registering the
36559860Sgdamore@opensolaris.org 		 * interrupt handler, we can copy gracefully with new multiport
36569860Sgdamore@opensolaris.org 		 * Cogent cards that decide to fix the hardware problem
36579860Sgdamore@opensolaris.org 		 */
36589860Sgdamore@opensolaris.org 		return (0);
36599860Sgdamore@opensolaris.org 	}
36609860Sgdamore@opensolaris.org 
36619860Sgdamore@opensolaris.org fail:
36629860Sgdamore@opensolaris.org 	cmn_err(CE_WARN, "dnet: Could not work around hardware interrupt"
36639860Sgdamore@opensolaris.org 	    " routing problem");
36649860Sgdamore@opensolaris.org 	return (0);
36659860Sgdamore@opensolaris.org }
36669860Sgdamore@opensolaris.org 
36679860Sgdamore@opensolaris.org /*
366810340Sstallion@opensolaris.org  * Call dnet_intr for all adapters on a multiport card
36699860Sgdamore@opensolaris.org  */
36709860Sgdamore@opensolaris.org static uint_t
dnet_hack_intr(struct hackintr_inf * hackintr_inf)36719860Sgdamore@opensolaris.org dnet_hack_intr(struct hackintr_inf *hackintr_inf)
36729860Sgdamore@opensolaris.org {
36739860Sgdamore@opensolaris.org 	int i;
36749860Sgdamore@opensolaris.org 	int claimed = DDI_INTR_UNCLAIMED;
36759860Sgdamore@opensolaris.org 
36769860Sgdamore@opensolaris.org 	/* Stop detaches while processing interrupts */
36779860Sgdamore@opensolaris.org 	mutex_enter(&hackintr_inf->lock);
36789860Sgdamore@opensolaris.org 
36799860Sgdamore@opensolaris.org 	for (i = 0; i < MAX_INST; i++) {
368010340Sstallion@opensolaris.org 		if (hackintr_inf->dnetps[i] &&
368110340Sstallion@opensolaris.org 		    dnet_intr((caddr_t)hackintr_inf->dnetps[i]) ==
368210340Sstallion@opensolaris.org 		    DDI_INTR_CLAIMED) {
36839860Sgdamore@opensolaris.org 			claimed = DDI_INTR_CLAIMED;
368410340Sstallion@opensolaris.org 		}
36859860Sgdamore@opensolaris.org 	}
36869860Sgdamore@opensolaris.org 	mutex_exit(&hackintr_inf->lock);
36879860Sgdamore@opensolaris.org 	return (claimed);
36889860Sgdamore@opensolaris.org }
36899860Sgdamore@opensolaris.org 
36909860Sgdamore@opensolaris.org /*
36919860Sgdamore@opensolaris.org  * This removes the detaching device from the table procesed by the hacked
36929860Sgdamore@opensolaris.org  * interrupt routine. Because the interrupts from all devices come in to the
36939860Sgdamore@opensolaris.org  * same interrupt handler, ALL devices must stop interrupting once the
36949860Sgdamore@opensolaris.org  * primary device detaches. This isn't a problem at present, because all
36959860Sgdamore@opensolaris.org  * instances of a device are detached when the driver is unloaded.
36969860Sgdamore@opensolaris.org  */
36979860Sgdamore@opensolaris.org static int
dnet_detach_hacked_interrupt(dev_info_t * devinfo)36989860Sgdamore@opensolaris.org dnet_detach_hacked_interrupt(dev_info_t *devinfo)
36999860Sgdamore@opensolaris.org {
37009860Sgdamore@opensolaris.org 	int i;
37019860Sgdamore@opensolaris.org 	struct hackintr_inf *hackintr_inf;
370210340Sstallion@opensolaris.org 	struct dnetinstance *altdnetp, *dnetp =
370310340Sstallion@opensolaris.org 	    ddi_get_driver_private(devinfo);
37049860Sgdamore@opensolaris.org 
37059860Sgdamore@opensolaris.org 	hackintr_inf = (struct hackintr_inf *)(uintptr_t)
37069860Sgdamore@opensolaris.org 	    ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
37079860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, hackintr_propname, 0);
37089860Sgdamore@opensolaris.org 
37099860Sgdamore@opensolaris.org 	/*
37109860Sgdamore@opensolaris.org 	 * No hackintr_inf implies hack was not required or the primary has
37119860Sgdamore@opensolaris.org 	 * detached, and our interrupts are already disabled
37129860Sgdamore@opensolaris.org 	 */
37139860Sgdamore@opensolaris.org 	if (!hackintr_inf) {
37149860Sgdamore@opensolaris.org 		/* remove the interrupt for the non-hacked case */
371510340Sstallion@opensolaris.org 		ddi_remove_intr(devinfo, 0, dnetp->icookie);
37169860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
37179860Sgdamore@opensolaris.org 	}
37189860Sgdamore@opensolaris.org 
37199860Sgdamore@opensolaris.org 	/* Remove this device from the handled table */
37209860Sgdamore@opensolaris.org 	mutex_enter(&hackintr_inf->lock);
37219860Sgdamore@opensolaris.org 	for (i = 0; i < MAX_INST; i++) {
372210340Sstallion@opensolaris.org 		if (hackintr_inf->dnetps[i] == dnetp) {
372310340Sstallion@opensolaris.org 			hackintr_inf->dnetps[i] = NULL;
37249860Sgdamore@opensolaris.org 			break;
37259860Sgdamore@opensolaris.org 		}
37269860Sgdamore@opensolaris.org 	}
37279860Sgdamore@opensolaris.org 
37289860Sgdamore@opensolaris.org 	mutex_exit(&hackintr_inf->lock);
37299860Sgdamore@opensolaris.org 
37309860Sgdamore@opensolaris.org 	/* Not the primary card, we are done */
37319860Sgdamore@opensolaris.org 	if (devinfo != hackintr_inf->devinfo)
37329860Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
37339860Sgdamore@opensolaris.org 
37349860Sgdamore@opensolaris.org 	/*
37359860Sgdamore@opensolaris.org 	 * This is the primary card. All remaining adapters on this device
37369860Sgdamore@opensolaris.org 	 * must have their interrupts disabled before we remove the handler
37379860Sgdamore@opensolaris.org 	 */
37389860Sgdamore@opensolaris.org 	for (i = 0; i < MAX_INST; i++) {
373910340Sstallion@opensolaris.org 		if ((altdnetp = hackintr_inf->dnetps[i]) != NULL) {
37409860Sgdamore@opensolaris.org 			altdnetp->interrupts_disabled = 1;
37419860Sgdamore@opensolaris.org 			ddi_put32(altdnetp->io_handle,
37429860Sgdamore@opensolaris.org 			    REG32(altdnetp->io_reg, INT_MASK_REG), 0);
37439860Sgdamore@opensolaris.org 		}
37449860Sgdamore@opensolaris.org 	}
37459860Sgdamore@opensolaris.org 
37469860Sgdamore@opensolaris.org 	/* It should now be safe to remove the interrupt handler */
37479860Sgdamore@opensolaris.org 
374810340Sstallion@opensolaris.org 	ddi_remove_intr(devinfo, 0, dnetp->icookie);
37499860Sgdamore@opensolaris.org 	mutex_destroy(&hackintr_inf->lock);
37509860Sgdamore@opensolaris.org 	/* XXX function return value ignored */
37519860Sgdamore@opensolaris.org 	(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
37529860Sgdamore@opensolaris.org 	    hackintr_propname);
37539860Sgdamore@opensolaris.org 	kmem_free(hackintr_inf, sizeof (struct hackintr_inf));
37549860Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
37559860Sgdamore@opensolaris.org }
37569860Sgdamore@opensolaris.org 
37579860Sgdamore@opensolaris.org /* do_phy() - called with intrlock held */
37589860Sgdamore@opensolaris.org static void
do_phy(struct dnetinstance * dnetp)375910340Sstallion@opensolaris.org do_phy(struct dnetinstance *dnetp)
37609860Sgdamore@opensolaris.org {
37619860Sgdamore@opensolaris.org 	dev_info_t *dip;
37629860Sgdamore@opensolaris.org 	LEAF_FORMAT *leaf = dnetp->sr.leaf + dnetp->leaf;
37639860Sgdamore@opensolaris.org 	media_block_t *block;
37649860Sgdamore@opensolaris.org 	int phy;
37659860Sgdamore@opensolaris.org 
37669860Sgdamore@opensolaris.org 	dip = dnetp->devinfo;
37679860Sgdamore@opensolaris.org 
37689860Sgdamore@opensolaris.org 	/*
37699860Sgdamore@opensolaris.org 	 * Find and configure the PHY media block. If NO PHY blocks are
37709860Sgdamore@opensolaris.org 	 * found on the SROM, but a PHY device is present, we assume the card
37719860Sgdamore@opensolaris.org 	 * is a legacy device, and that there is ONLY a PHY interface on the
37729860Sgdamore@opensolaris.org 	 * card (ie, no BNC or AUI, and 10BaseT is implemented by the PHY
37739860Sgdamore@opensolaris.org 	 */
37749860Sgdamore@opensolaris.org 
37759860Sgdamore@opensolaris.org 	for (block = leaf->block + leaf->block_count -1;
37769860Sgdamore@opensolaris.org 	    block >= leaf->block; block --) {
37779860Sgdamore@opensolaris.org 		if (block->type == 3 || block->type == 1) {
37789860Sgdamore@opensolaris.org 			leaf->mii_block = block;
37799860Sgdamore@opensolaris.org 			break;
37809860Sgdamore@opensolaris.org 		}
37819860Sgdamore@opensolaris.org 	}
37829860Sgdamore@opensolaris.org 
37839860Sgdamore@opensolaris.org 	/*
37849860Sgdamore@opensolaris.org 	 * If no MII block, select default, and hope this configuration will
37859860Sgdamore@opensolaris.org 	 * allow the phy to be read/written if it is present
37869860Sgdamore@opensolaris.org 	 */
37879860Sgdamore@opensolaris.org 	dnetp->selected_media_block = leaf->mii_block ?
37889860Sgdamore@opensolaris.org 	    leaf->mii_block : leaf->default_block;
37899860Sgdamore@opensolaris.org 
379010340Sstallion@opensolaris.org 	setup_block(dnetp);
37919860Sgdamore@opensolaris.org 	/* XXX function return value ignored */
37929860Sgdamore@opensolaris.org 	(void) mii_create(dip, dnet_mii_write, dnet_mii_read, &dnetp->mii);
37939860Sgdamore@opensolaris.org 
37949860Sgdamore@opensolaris.org 	/*
37959860Sgdamore@opensolaris.org 	 * We try PHY 0 LAST because it is less likely to be connected
37969860Sgdamore@opensolaris.org 	 */
37979860Sgdamore@opensolaris.org 	for (phy = 1; phy < 33; phy++)
37989860Sgdamore@opensolaris.org 		if (mii_probe_phy(dnetp->mii, phy % 32) == MII_SUCCESS &&
37999860Sgdamore@opensolaris.org 		    mii_init_phy(dnetp->mii, phy % 32) == MII_SUCCESS) {
38009860Sgdamore@opensolaris.org #ifdef DNETDEBUG
38019860Sgdamore@opensolaris.org 			if (dnetdebug & DNETSENSE)
38029860Sgdamore@opensolaris.org 				cmn_err(CE_NOTE, "dnet: "
38039860Sgdamore@opensolaris.org 				    "PHY at address %d", phy % 32);
38049860Sgdamore@opensolaris.org #endif
38059860Sgdamore@opensolaris.org 			dnetp->phyaddr = phy % 32;
38069860Sgdamore@opensolaris.org 			if (!leaf->mii_block) {
38079860Sgdamore@opensolaris.org 				/* Legacy card, change the leaf node */
38089860Sgdamore@opensolaris.org 				set_leaf(&dnetp->sr, &leaf_phylegacy);
38099860Sgdamore@opensolaris.org 			}
38109860Sgdamore@opensolaris.org 			return;
38119860Sgdamore@opensolaris.org 		}
38129860Sgdamore@opensolaris.org #ifdef DNETDEBUG
38139860Sgdamore@opensolaris.org 	if (dnetdebug & DNETSENSE)
38149860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "dnet: No PHY found");
38159860Sgdamore@opensolaris.org #endif
38169860Sgdamore@opensolaris.org }
38179860Sgdamore@opensolaris.org 
38189860Sgdamore@opensolaris.org static ushort_t
dnet_mii_read(dev_info_t * dip,int phy_addr,int reg_num)38199860Sgdamore@opensolaris.org dnet_mii_read(dev_info_t *dip, int phy_addr, int reg_num)
38209860Sgdamore@opensolaris.org {
38219860Sgdamore@opensolaris.org 	struct dnetinstance *dnetp;
38229860Sgdamore@opensolaris.org 
38239860Sgdamore@opensolaris.org 	uint32_t command_word;
38249860Sgdamore@opensolaris.org 	uint32_t tmp;
38259860Sgdamore@opensolaris.org 	uint32_t data = 0;
38269860Sgdamore@opensolaris.org 	int i;
38279860Sgdamore@opensolaris.org 	int bits_in_ushort = ((sizeof (ushort_t))*8);
38289860Sgdamore@opensolaris.org 	int turned_around = 0;
38299860Sgdamore@opensolaris.org 
383010340Sstallion@opensolaris.org 	dnetp = ddi_get_driver_private(dip);
38319860Sgdamore@opensolaris.org 
38329860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
38339860Sgdamore@opensolaris.org 	/* Write Preamble */
38349860Sgdamore@opensolaris.org 	write_mii(dnetp, MII_PRE, 2*bits_in_ushort);
38359860Sgdamore@opensolaris.org 
38369860Sgdamore@opensolaris.org 	/* Prepare command word */
38379860Sgdamore@opensolaris.org 	command_word = (uint32_t)phy_addr << MII_PHY_ADDR_ALIGN;
38389860Sgdamore@opensolaris.org 	command_word |= (uint32_t)reg_num << MII_REG_ADDR_ALIGN;
38399860Sgdamore@opensolaris.org 	command_word |= MII_READ_FRAME;
38409860Sgdamore@opensolaris.org 
38419860Sgdamore@opensolaris.org 	write_mii(dnetp, command_word, bits_in_ushort-2);
38429860Sgdamore@opensolaris.org 
38439860Sgdamore@opensolaris.org 	mii_tristate(dnetp);
38449860Sgdamore@opensolaris.org 
38459860Sgdamore@opensolaris.org 	/* Check that the PHY generated a zero bit the 2nd clock */
38469860Sgdamore@opensolaris.org 	tmp = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, ETHER_ROM_REG));
38479860Sgdamore@opensolaris.org 
38489860Sgdamore@opensolaris.org 	turned_around = (tmp & MII_DATA_IN) ? 0 : 1;
38499860Sgdamore@opensolaris.org 
38509860Sgdamore@opensolaris.org 	/* read data WORD */
38519860Sgdamore@opensolaris.org 	for (i = 0; i < bits_in_ushort; i++) {
38529860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
38539860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, ETHER_ROM_REG), MII_READ);
38549860Sgdamore@opensolaris.org 		drv_usecwait(MII_DELAY);
38559860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
38569860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, ETHER_ROM_REG), MII_READ | MII_CLOCK);
38579860Sgdamore@opensolaris.org 		drv_usecwait(MII_DELAY);
38589860Sgdamore@opensolaris.org 		tmp = ddi_get32(dnetp->io_handle,
38599860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, ETHER_ROM_REG));
38609860Sgdamore@opensolaris.org 		drv_usecwait(MII_DELAY);
38619860Sgdamore@opensolaris.org 		data = (data << 1) | (tmp >> MII_DATA_IN_POSITION) & 0x0001;
38629860Sgdamore@opensolaris.org 	}
38639860Sgdamore@opensolaris.org 
38649860Sgdamore@opensolaris.org 	mii_tristate(dnetp);
38659860Sgdamore@opensolaris.org 	return (turned_around ? data: -1);
38669860Sgdamore@opensolaris.org }
38679860Sgdamore@opensolaris.org 
38689860Sgdamore@opensolaris.org static void
dnet_mii_write(dev_info_t * dip,int phy_addr,int reg_num,int reg_dat)38699860Sgdamore@opensolaris.org dnet_mii_write(dev_info_t *dip, int phy_addr, int reg_num, int reg_dat)
38709860Sgdamore@opensolaris.org {
38719860Sgdamore@opensolaris.org 	struct dnetinstance *dnetp;
38729860Sgdamore@opensolaris.org 	uint32_t command_word;
38739860Sgdamore@opensolaris.org 	int bits_in_ushort = ((sizeof (ushort_t))*8);
38749860Sgdamore@opensolaris.org 
387510340Sstallion@opensolaris.org 	dnetp = ddi_get_driver_private(dip);
38769860Sgdamore@opensolaris.org 
38779860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
38789860Sgdamore@opensolaris.org 	write_mii(dnetp, MII_PRE, 2*bits_in_ushort);
38799860Sgdamore@opensolaris.org 
38809860Sgdamore@opensolaris.org 	/* Prepare command word */
38819860Sgdamore@opensolaris.org 	command_word = ((uint32_t)phy_addr << MII_PHY_ADDR_ALIGN);
38829860Sgdamore@opensolaris.org 	command_word |= ((uint32_t)reg_num << MII_REG_ADDR_ALIGN);
38839860Sgdamore@opensolaris.org 	command_word |= (MII_WRITE_FRAME | (uint32_t)reg_dat);
38849860Sgdamore@opensolaris.org 
38859860Sgdamore@opensolaris.org 	write_mii(dnetp, command_word, 2*bits_in_ushort);
38869860Sgdamore@opensolaris.org 	mii_tristate(dnetp);
38879860Sgdamore@opensolaris.org }
38889860Sgdamore@opensolaris.org 
38899860Sgdamore@opensolaris.org /*
38909860Sgdamore@opensolaris.org  * Write data size bits from mii_data to the MII control lines.
38919860Sgdamore@opensolaris.org  */
38929860Sgdamore@opensolaris.org static void
write_mii(struct dnetinstance * dnetp,uint32_t mii_data,int data_size)38939860Sgdamore@opensolaris.org write_mii(struct dnetinstance *dnetp, uint32_t mii_data, int data_size)
38949860Sgdamore@opensolaris.org {
38959860Sgdamore@opensolaris.org 	int i;
38969860Sgdamore@opensolaris.org 	uint32_t dbit;
38979860Sgdamore@opensolaris.org 
38989860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
38999860Sgdamore@opensolaris.org 	for (i = data_size; i > 0; i--) {
39009860Sgdamore@opensolaris.org 		dbit = ((mii_data >>
39019860Sgdamore@opensolaris.org 		    (31 - MII_WRITE_DATA_POSITION)) & MII_WRITE_DATA);
39029860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
39039860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, ETHER_ROM_REG),
39049860Sgdamore@opensolaris.org 		    MII_WRITE | dbit);
39059860Sgdamore@opensolaris.org 		drv_usecwait(MII_DELAY);
39069860Sgdamore@opensolaris.org 		ddi_put32(dnetp->io_handle,
39079860Sgdamore@opensolaris.org 		    REG32(dnetp->io_reg, ETHER_ROM_REG),
39089860Sgdamore@opensolaris.org 		    MII_WRITE | MII_CLOCK | dbit);
39099860Sgdamore@opensolaris.org 		drv_usecwait(MII_DELAY);
39109860Sgdamore@opensolaris.org 		mii_data <<= 1;
39119860Sgdamore@opensolaris.org 	}
39129860Sgdamore@opensolaris.org }
39139860Sgdamore@opensolaris.org 
39149860Sgdamore@opensolaris.org /*
39159860Sgdamore@opensolaris.org  * Put the MDIO port in tri-state for the turn around bits
39169860Sgdamore@opensolaris.org  * in MII read and at end of MII management sequence.
39179860Sgdamore@opensolaris.org  */
39189860Sgdamore@opensolaris.org static void
mii_tristate(struct dnetinstance * dnetp)39199860Sgdamore@opensolaris.org mii_tristate(struct dnetinstance *dnetp)
39209860Sgdamore@opensolaris.org {
39219860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
39229860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, ETHER_ROM_REG),
39239860Sgdamore@opensolaris.org 	    MII_WRITE_TS);
39249860Sgdamore@opensolaris.org 	drv_usecwait(MII_DELAY);
39259860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, ETHER_ROM_REG),
39269860Sgdamore@opensolaris.org 	    MII_WRITE_TS | MII_CLOCK);
39279860Sgdamore@opensolaris.org 	drv_usecwait(MII_DELAY);
39289860Sgdamore@opensolaris.org }
39299860Sgdamore@opensolaris.org 
39309860Sgdamore@opensolaris.org 
39319860Sgdamore@opensolaris.org static void
set_leaf(SROM_FORMAT * sr,LEAF_FORMAT * leaf)39329860Sgdamore@opensolaris.org set_leaf(SROM_FORMAT *sr, LEAF_FORMAT *leaf)
39339860Sgdamore@opensolaris.org {
39349860Sgdamore@opensolaris.org 	if (sr->leaf && !sr->leaf->is_static)
39359860Sgdamore@opensolaris.org 		kmem_free(sr->leaf, sr->adapters * sizeof (LEAF_FORMAT));
39369860Sgdamore@opensolaris.org 	sr->leaf = leaf;
39379860Sgdamore@opensolaris.org }
39389860Sgdamore@opensolaris.org 
39399860Sgdamore@opensolaris.org /*
39409860Sgdamore@opensolaris.org  * Callback from MII module. Makes sure that the CSR registers are
39419860Sgdamore@opensolaris.org  * configured properly if the PHY changes mode.
39429860Sgdamore@opensolaris.org  */
39439860Sgdamore@opensolaris.org /* ARGSUSED */
39449860Sgdamore@opensolaris.org /* dnet_mii_link_cb - called with intrlock held */
39459860Sgdamore@opensolaris.org static void
dnet_mii_link_cb(dev_info_t * dip,int phy,enum mii_phy_state state)39469860Sgdamore@opensolaris.org dnet_mii_link_cb(dev_info_t *dip, int phy, enum mii_phy_state state)
39479860Sgdamore@opensolaris.org {
394810340Sstallion@opensolaris.org 	struct dnetinstance *dnetp = ddi_get_driver_private(dip);
39499860Sgdamore@opensolaris.org 	LEAF_FORMAT *leaf;
39509860Sgdamore@opensolaris.org 
39519860Sgdamore@opensolaris.org 	ASSERT(MUTEX_HELD(&dnetp->intrlock));
395210340Sstallion@opensolaris.org 
39539860Sgdamore@opensolaris.org 	leaf = dnetp->sr.leaf + dnetp->leaf;
39549860Sgdamore@opensolaris.org 	if (state == phy_state_linkup) {
39559860Sgdamore@opensolaris.org 		dnetp->mii_up = 1;
395610340Sstallion@opensolaris.org 
395710340Sstallion@opensolaris.org 		(void) mii_getspeed(dnetp->mii, dnetp->phyaddr,
395810340Sstallion@opensolaris.org 		    &dnetp->mii_speed, &dnetp->mii_duplex);
395910340Sstallion@opensolaris.org 
39609860Sgdamore@opensolaris.org 		dnetp->selected_media_block = leaf->mii_block;
396110340Sstallion@opensolaris.org 		setup_block(dnetp);
39629860Sgdamore@opensolaris.org 	} else {
39639860Sgdamore@opensolaris.org 		/* NEEDSWORK: Probably can call find_active_media here */
39649860Sgdamore@opensolaris.org 		dnetp->mii_up = 0;
396510340Sstallion@opensolaris.org 
396610340Sstallion@opensolaris.org 		if (leaf->default_block->media_code == MEDIA_MII)
396710340Sstallion@opensolaris.org 			dnetp->selected_media_block = leaf->default_block;
396810340Sstallion@opensolaris.org 		setup_block(dnetp);
396910340Sstallion@opensolaris.org 	}
397011198Sstallion@opensolaris.org 
397111198Sstallion@opensolaris.org 	if (dnetp->running) {
397211198Sstallion@opensolaris.org 		mac_link_update(dnetp->mac_handle,
397311198Sstallion@opensolaris.org 		    (dnetp->mii_up ? LINK_STATE_UP : LINK_STATE_DOWN));
39749860Sgdamore@opensolaris.org 	}
39759860Sgdamore@opensolaris.org }
39769860Sgdamore@opensolaris.org 
39779860Sgdamore@opensolaris.org /*
39789860Sgdamore@opensolaris.org  * SROM parsing routines.
39799860Sgdamore@opensolaris.org  * Refer to the Digital 3.03 SROM spec while reading this! (references refer
39809860Sgdamore@opensolaris.org  * to this document)
39819860Sgdamore@opensolaris.org  * Where possible ALL vendor specific changes should be localised here. The
39829860Sgdamore@opensolaris.org  * SROM data should be capable of describing any programmatic irregularities
39839860Sgdamore@opensolaris.org  * of DNET cards (via SIA or GP registers, in particular), so vendor specific
39849860Sgdamore@opensolaris.org  * code elsewhere should not be required
39859860Sgdamore@opensolaris.org  */
39869860Sgdamore@opensolaris.org static void
dnet_parse_srom(struct dnetinstance * dnetp,SROM_FORMAT * sr,uchar_t * vi)39879860Sgdamore@opensolaris.org dnet_parse_srom(struct dnetinstance *dnetp, SROM_FORMAT *sr, uchar_t *vi)
39889860Sgdamore@opensolaris.org {
39899860Sgdamore@opensolaris.org 	uint32_t ether_mfg = 0;
39909860Sgdamore@opensolaris.org 	int i;
39919860Sgdamore@opensolaris.org 	uchar_t *p;
39929860Sgdamore@opensolaris.org 
39939860Sgdamore@opensolaris.org 	if (!ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
39949860Sgdamore@opensolaris.org 	    DDI_PROP_DONTPASS, "no_sromconfig", 0))
39959860Sgdamore@opensolaris.org 		dnetp->sr.init_from_srom = check_srom_valid(vi);
39969860Sgdamore@opensolaris.org 
39979860Sgdamore@opensolaris.org 	if (dnetp->sr.init_from_srom && dnetp->board_type != DEVICE_ID_21040) {
39989860Sgdamore@opensolaris.org 		/* Section 2/3: General SROM Format/ ID Block */
39999860Sgdamore@opensolaris.org 		p = vi+18;
40009860Sgdamore@opensolaris.org 		sr->version = *p++;
40019860Sgdamore@opensolaris.org 		sr->adapters = *p++;
40029860Sgdamore@opensolaris.org 
40039860Sgdamore@opensolaris.org 		sr->leaf =
40049860Sgdamore@opensolaris.org 		    kmem_zalloc(sr->adapters * sizeof (LEAF_FORMAT), KM_SLEEP);
40059860Sgdamore@opensolaris.org 		for (i = 0; i < 6; i++)
40069860Sgdamore@opensolaris.org 			sr->netaddr[i] = *p++;
40079860Sgdamore@opensolaris.org 
40089860Sgdamore@opensolaris.org 		for (i = 0; i < sr->adapters; i++) {
40099860Sgdamore@opensolaris.org 			uchar_t devno = *p++;
40109860Sgdamore@opensolaris.org 			uint16_t offset = *p++;
40119860Sgdamore@opensolaris.org 			offset |= *p++ << 8;
40129860Sgdamore@opensolaris.org 			sr->leaf[i].device_number = devno;
40139860Sgdamore@opensolaris.org 			parse_controller_leaf(dnetp, sr->leaf+i, vi+offset);
40149860Sgdamore@opensolaris.org 		}
40159860Sgdamore@opensolaris.org 		/*
40169860Sgdamore@opensolaris.org 		 * 'Orrible hack for cogent cards. The 6911A board seems to
40179860Sgdamore@opensolaris.org 		 * have an incorrect SROM. (From the OEMDEMO program
40189860Sgdamore@opensolaris.org 		 * supplied by cogent, it seems that the ROM matches a setup
40199860Sgdamore@opensolaris.org 		 * or a board with a QSI or ICS PHY.
40209860Sgdamore@opensolaris.org 		 */
40219860Sgdamore@opensolaris.org 		for (i = 0; i < 3; i++)
40229860Sgdamore@opensolaris.org 			ether_mfg = (ether_mfg << 8) | sr->netaddr[i];
40239860Sgdamore@opensolaris.org 
40249860Sgdamore@opensolaris.org 		if (ether_mfg == ADAPTEC_ETHER) {
40259860Sgdamore@opensolaris.org 			static uint16_t cogent_gprseq[] = {0x821, 0};
40269860Sgdamore@opensolaris.org 			switch (vi[COGENT_SROM_ID]) {
40279860Sgdamore@opensolaris.org 			case COGENT_ANA6911A_C:
40289860Sgdamore@opensolaris.org 			case COGENT_ANA6911AC_C:
40299860Sgdamore@opensolaris.org #ifdef DNETDEBUG
40309860Sgdamore@opensolaris.org 				if (dnetdebug & DNETTRACE)
40319860Sgdamore@opensolaris.org 					cmn_err(CE_WARN,
40329860Sgdamore@opensolaris.org 					    "Suspected bad GPR sequence."
40339860Sgdamore@opensolaris.org 					    " Making a guess (821,0)");
40349860Sgdamore@opensolaris.org #endif
40359860Sgdamore@opensolaris.org 
40369860Sgdamore@opensolaris.org 				/* XXX function return value ignored */
40379860Sgdamore@opensolaris.org 				(void) ddi_prop_update_byte_array(
40389860Sgdamore@opensolaris.org 				    DDI_DEV_T_NONE, dnetp->devinfo,
40399860Sgdamore@opensolaris.org 				    "gpr-sequence", (uchar_t *)cogent_gprseq,
40409860Sgdamore@opensolaris.org 				    sizeof (cogent_gprseq));
40419860Sgdamore@opensolaris.org 				break;
40429860Sgdamore@opensolaris.org 			}
40439860Sgdamore@opensolaris.org 		}
40449860Sgdamore@opensolaris.org 	} else {
40459860Sgdamore@opensolaris.org 		/*
40469860Sgdamore@opensolaris.org 		 * Adhoc SROM, check for some cards which need special handling
40479860Sgdamore@opensolaris.org 		 * Assume vendor info contains ether address in first six bytes
40489860Sgdamore@opensolaris.org 		 */
40499860Sgdamore@opensolaris.org 
40509860Sgdamore@opensolaris.org 		uchar_t *mac = vi + ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
40519860Sgdamore@opensolaris.org 		    DDI_PROP_DONTPASS, macoffset_propname, 0);
40529860Sgdamore@opensolaris.org 
40539860Sgdamore@opensolaris.org 		for (i = 0; i < 6; i++)
40549860Sgdamore@opensolaris.org 			sr->netaddr[i] = mac[i];
40559860Sgdamore@opensolaris.org 
40569860Sgdamore@opensolaris.org 		if (dnetp->board_type == DEVICE_ID_21140) {
40579860Sgdamore@opensolaris.org 			for (i = 0; i < 3; i++)
40589860Sgdamore@opensolaris.org 				ether_mfg = (ether_mfg << 8) | mac[i];
40599860Sgdamore@opensolaris.org 
40609860Sgdamore@opensolaris.org 			switch (ether_mfg) {
40619860Sgdamore@opensolaris.org 			case ASANTE_ETHER:
40629860Sgdamore@opensolaris.org 				dnetp->vendor_21140 = ASANTE_TYPE;
40639860Sgdamore@opensolaris.org 				dnetp->vendor_revision = 0;
40649860Sgdamore@opensolaris.org 				set_leaf(sr, &leaf_asante);
40659860Sgdamore@opensolaris.org 				sr->adapters = 1;
40669860Sgdamore@opensolaris.org 				break;
40679860Sgdamore@opensolaris.org 
40689860Sgdamore@opensolaris.org 			case COGENT_ETHER:
40699860Sgdamore@opensolaris.org 			case ADAPTEC_ETHER:
40709860Sgdamore@opensolaris.org 				dnetp->vendor_21140 = COGENT_EM_TYPE;
40719860Sgdamore@opensolaris.org 				dnetp->vendor_revision =
40729860Sgdamore@opensolaris.org 				    vi[VENDOR_REVISION_OFFSET];
40739860Sgdamore@opensolaris.org 				set_leaf(sr, &leaf_cogent_100);
40749860Sgdamore@opensolaris.org 				sr->adapters = 1;
40759860Sgdamore@opensolaris.org 				break;
40769860Sgdamore@opensolaris.org 
40779860Sgdamore@opensolaris.org 			default:
40789860Sgdamore@opensolaris.org 				dnetp->vendor_21140 = DEFAULT_TYPE;
40799860Sgdamore@opensolaris.org 				dnetp->vendor_revision = 0;
40809860Sgdamore@opensolaris.org 				set_leaf(sr, &leaf_default_100);
40819860Sgdamore@opensolaris.org 				sr->adapters = 1;
40829860Sgdamore@opensolaris.org 				break;
40839860Sgdamore@opensolaris.org 			}
40849860Sgdamore@opensolaris.org 		} else if (dnetp->board_type == DEVICE_ID_21041) {
40859860Sgdamore@opensolaris.org 			set_leaf(sr, &leaf_21041);
40869860Sgdamore@opensolaris.org 		} else if (dnetp->board_type == DEVICE_ID_21040) {
40879860Sgdamore@opensolaris.org 			set_leaf(sr, &leaf_21040);
40889860Sgdamore@opensolaris.org 		}
40899860Sgdamore@opensolaris.org 	}
40909860Sgdamore@opensolaris.org }
40919860Sgdamore@opensolaris.org 
40929860Sgdamore@opensolaris.org /* Section 4.2, 4.3, 4.4, 4.5 */
40939860Sgdamore@opensolaris.org static void
parse_controller_leaf(struct dnetinstance * dnetp,LEAF_FORMAT * leaf,uchar_t * vi)40949860Sgdamore@opensolaris.org parse_controller_leaf(struct dnetinstance *dnetp, LEAF_FORMAT *leaf,
40959860Sgdamore@opensolaris.org 	uchar_t *vi)
40969860Sgdamore@opensolaris.org {
40979860Sgdamore@opensolaris.org 	int i;
40989860Sgdamore@opensolaris.org 
40999860Sgdamore@opensolaris.org 	leaf->selected_contype = *vi++;
41009860Sgdamore@opensolaris.org 	leaf->selected_contype |= *vi++ << 8;
41019860Sgdamore@opensolaris.org 
41029860Sgdamore@opensolaris.org 	if (dnetp->board_type == DEVICE_ID_21140) /* Sect. 4.3 */
41039860Sgdamore@opensolaris.org 		leaf->gpr = *vi++;
41049860Sgdamore@opensolaris.org 
41059860Sgdamore@opensolaris.org 	leaf->block_count = *vi++;
41069860Sgdamore@opensolaris.org 
41079860Sgdamore@opensolaris.org 	if (leaf->block_count > MAX_MEDIA) {
41089860Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "dnet: Too many media in SROM!");
41099860Sgdamore@opensolaris.org 		leaf->block_count = 1;
41109860Sgdamore@opensolaris.org 	}
41119860Sgdamore@opensolaris.org 	for (i = 0; i <= leaf->block_count; i++) {
41129860Sgdamore@opensolaris.org 		vi = parse_media_block(dnetp, leaf->block + i, vi);
41139860Sgdamore@opensolaris.org 		if (leaf->block[i].command & CMD_DEFAULT_MEDIUM)
41149860Sgdamore@opensolaris.org 			leaf->default_block = leaf->block+i;
41159860Sgdamore@opensolaris.org 	}
41169860Sgdamore@opensolaris.org 	/* No explicit default block: use last in the ROM */
41179860Sgdamore@opensolaris.org 	if (leaf->default_block == NULL)
41189860Sgdamore@opensolaris.org 		leaf->default_block = leaf->block + leaf->block_count -1;
41199860Sgdamore@opensolaris.org 
41209860Sgdamore@opensolaris.org }
41219860Sgdamore@opensolaris.org 
41229860Sgdamore@opensolaris.org static uchar_t *
parse_media_block(struct dnetinstance * dnetp,media_block_t * block,uchar_t * vi)41239860Sgdamore@opensolaris.org parse_media_block(struct dnetinstance *dnetp, media_block_t *block, uchar_t *vi)
41249860Sgdamore@opensolaris.org {
41259860Sgdamore@opensolaris.org 	int i;
41269860Sgdamore@opensolaris.org 
41279860Sgdamore@opensolaris.org 	/*
41289860Sgdamore@opensolaris.org 	 * There are three kinds of media block we need to worry about:
41299860Sgdamore@opensolaris.org 	 * The 21041 blocks.
41309860Sgdamore@opensolaris.org 	 * 21140 blocks from a version 1 SROM
41319860Sgdamore@opensolaris.org 	 * 2114[023] block from a version 3 SROM
41329860Sgdamore@opensolaris.org 	 */
41339860Sgdamore@opensolaris.org 
41349860Sgdamore@opensolaris.org 	if (dnetp->board_type == DEVICE_ID_21041) {
41359860Sgdamore@opensolaris.org 		/* Section 4.2 */
41369860Sgdamore@opensolaris.org 		block->media_code = *vi & 0x3f;
41379860Sgdamore@opensolaris.org 		block->type = 2;
41389860Sgdamore@opensolaris.org 		if (*vi++ & 0x40) {
41399860Sgdamore@opensolaris.org 			block->un.sia.csr13 = *vi++;
41409860Sgdamore@opensolaris.org 			block->un.sia.csr13 |= *vi++ << 8;
41419860Sgdamore@opensolaris.org 			block->un.sia.csr14 = *vi++;
41429860Sgdamore@opensolaris.org 			block->un.sia.csr14 |= *vi++ << 8;
41439860Sgdamore@opensolaris.org 			block->un.sia.csr15 = *vi++;
41449860Sgdamore@opensolaris.org 			block->un.sia.csr15 |= *vi++ << 8;
41459860Sgdamore@opensolaris.org 		} else {
41469860Sgdamore@opensolaris.org 			/* No media data (csrs 13,14,15). Insert defaults */
41479860Sgdamore@opensolaris.org 			switch (block->media_code) {
41489860Sgdamore@opensolaris.org 			case MEDIA_TP:
41499860Sgdamore@opensolaris.org 				block->un.sia.csr13 = 0xef01;
41509860Sgdamore@opensolaris.org 				block->un.sia.csr14 = 0x7f3f;
41519860Sgdamore@opensolaris.org 				block->un.sia.csr15 = 0x0008;
41529860Sgdamore@opensolaris.org 				break;
41539860Sgdamore@opensolaris.org 			case MEDIA_TP_FD:
41549860Sgdamore@opensolaris.org 				block->un.sia.csr13 = 0xef01;
41559860Sgdamore@opensolaris.org 				block->un.sia.csr14 = 0x7f3d;
41569860Sgdamore@opensolaris.org 				block->un.sia.csr15 = 0x0008;
41579860Sgdamore@opensolaris.org 				break;
41589860Sgdamore@opensolaris.org 			case MEDIA_BNC:
41599860Sgdamore@opensolaris.org 				block->un.sia.csr13 = 0xef09;
41609860Sgdamore@opensolaris.org 				block->un.sia.csr14 = 0x0705;
41619860Sgdamore@opensolaris.org 				block->un.sia.csr15 = 0x0006;
41629860Sgdamore@opensolaris.org 				break;
41639860Sgdamore@opensolaris.org 			case MEDIA_AUI:
41649860Sgdamore@opensolaris.org 				block->un.sia.csr13 = 0xef09;
41659860Sgdamore@opensolaris.org 				block->un.sia.csr14 = 0x0705;
41669860Sgdamore@opensolaris.org 				block->un.sia.csr15 = 0x000e;
41679860Sgdamore@opensolaris.org 				break;
41689860Sgdamore@opensolaris.org 			}
41699860Sgdamore@opensolaris.org 		}
41709860Sgdamore@opensolaris.org 	} else  if (*vi & 0x80) {  /* Extended format: Section 4.3.2.2 */
41719860Sgdamore@opensolaris.org 		int blocklen = *vi++ & 0x7f;
41729860Sgdamore@opensolaris.org 		block->type = *vi++;
41739860Sgdamore@opensolaris.org 		switch (block->type) {
41749860Sgdamore@opensolaris.org 		case 0: /* "non-MII": Section 4.3.2.2.1 */
41759860Sgdamore@opensolaris.org 			block->media_code = (*vi++) & 0x3f;
41769860Sgdamore@opensolaris.org 			block->gprseqlen = 1;
41779860Sgdamore@opensolaris.org 			block->gprseq[0] = *vi++;
41789860Sgdamore@opensolaris.org 			block->command = *vi++;
41799860Sgdamore@opensolaris.org 			block->command |= *vi++ << 8;
41809860Sgdamore@opensolaris.org 			break;
41819860Sgdamore@opensolaris.org 
41829860Sgdamore@opensolaris.org 		case 1: /* MII/PHY: Section 4.3.2.2.2 */
41839860Sgdamore@opensolaris.org 			block->command = CMD_PS;
41849860Sgdamore@opensolaris.org 			block->media_code = MEDIA_MII;
41859860Sgdamore@opensolaris.org 				/* This is whats needed in CSR6 */
41869860Sgdamore@opensolaris.org 
41879860Sgdamore@opensolaris.org 			block->un.mii.phy_num = *vi++;
41889860Sgdamore@opensolaris.org 			block->gprseqlen = *vi++;
41899860Sgdamore@opensolaris.org 
41909860Sgdamore@opensolaris.org 			for (i = 0; i < block->gprseqlen; i++)
41919860Sgdamore@opensolaris.org 				block->gprseq[i] = *vi++;
41929860Sgdamore@opensolaris.org 			block->rstseqlen = *vi++;
41939860Sgdamore@opensolaris.org 			for (i = 0; i < block->rstseqlen; i++)
41949860Sgdamore@opensolaris.org 				block->rstseq[i] = *vi++;
41959860Sgdamore@opensolaris.org 
41969860Sgdamore@opensolaris.org 			block->un.mii.mediacaps = *vi++;
41979860Sgdamore@opensolaris.org 			block->un.mii.mediacaps |= *vi++ << 8;
41989860Sgdamore@opensolaris.org 			block->un.mii.nwayadvert = *vi++;
41999860Sgdamore@opensolaris.org 			block->un.mii.nwayadvert |= *vi++ << 8;
42009860Sgdamore@opensolaris.org 			block->un.mii.fdxmask = *vi++;
42019860Sgdamore@opensolaris.org 			block->un.mii.fdxmask |= *vi++ << 8;
42029860Sgdamore@opensolaris.org 			block->un.mii.ttmmask = *vi++;
42039860Sgdamore@opensolaris.org 			block->un.mii.ttmmask |= *vi++ << 8;
42049860Sgdamore@opensolaris.org 			break;
42059860Sgdamore@opensolaris.org 
42069860Sgdamore@opensolaris.org 		case 2: /* SIA Media: Section 4.4.2.1.1 */
42079860Sgdamore@opensolaris.org 			block->media_code = *vi & 0x3f;
42089860Sgdamore@opensolaris.org 			if (*vi++ & 0x40) {
42099860Sgdamore@opensolaris.org 				block->un.sia.csr13 = *vi++;
42109860Sgdamore@opensolaris.org 				block->un.sia.csr13 |= *vi++ << 8;
42119860Sgdamore@opensolaris.org 				block->un.sia.csr14 = *vi++;
42129860Sgdamore@opensolaris.org 				block->un.sia.csr14 |= *vi++ << 8;
42139860Sgdamore@opensolaris.org 				block->un.sia.csr15 = *vi++;
42149860Sgdamore@opensolaris.org 				block->un.sia.csr15 |= *vi++ << 8;
42159860Sgdamore@opensolaris.org 			} else {
42169860Sgdamore@opensolaris.org 				/*
42179860Sgdamore@opensolaris.org 				 * SIA values not provided by SROM; provide
42189860Sgdamore@opensolaris.org 				 * defaults. See appendix D of 2114[23] manuals.
42199860Sgdamore@opensolaris.org 				 */
42209860Sgdamore@opensolaris.org 				switch (block->media_code) {
42219860Sgdamore@opensolaris.org 				case MEDIA_BNC:
42229860Sgdamore@opensolaris.org 					block->un.sia.csr13 = 0x0009;
42239860Sgdamore@opensolaris.org 					block->un.sia.csr14 = 0x0705;
42249860Sgdamore@opensolaris.org 					block->un.sia.csr15 = 0x0000;
42259860Sgdamore@opensolaris.org 					break;
42269860Sgdamore@opensolaris.org 				case MEDIA_AUI:
42279860Sgdamore@opensolaris.org 					block->un.sia.csr13 = 0x0009;
42289860Sgdamore@opensolaris.org 					block->un.sia.csr14 = 0x0705;
42299860Sgdamore@opensolaris.org 					block->un.sia.csr15 = 0x0008;
42309860Sgdamore@opensolaris.org 					break;
42319860Sgdamore@opensolaris.org 				case MEDIA_TP:
42329860Sgdamore@opensolaris.org 					block->un.sia.csr13 = 0x0001;
42339860Sgdamore@opensolaris.org 					block->un.sia.csr14 = 0x7f3f;
42349860Sgdamore@opensolaris.org 					block->un.sia.csr15 = 0x0000;
42359860Sgdamore@opensolaris.org 					break;
42369860Sgdamore@opensolaris.org 				case MEDIA_TP_FD:
42379860Sgdamore@opensolaris.org 					block->un.sia.csr13 = 0x0001;
42389860Sgdamore@opensolaris.org 					block->un.sia.csr14 = 0x7f3d;
42399860Sgdamore@opensolaris.org 					block->un.sia.csr15 = 0x0000;
42409860Sgdamore@opensolaris.org 					break;
42419860Sgdamore@opensolaris.org 				default:
42429860Sgdamore@opensolaris.org 					block->un.sia.csr13 = 0x0000;
42439860Sgdamore@opensolaris.org 					block->un.sia.csr14 = 0x0000;
42449860Sgdamore@opensolaris.org 					block->un.sia.csr15 = 0x0000;
42459860Sgdamore@opensolaris.org 				}
42469860Sgdamore@opensolaris.org 			}
42479860Sgdamore@opensolaris.org 
42489860Sgdamore@opensolaris.org 			/* Treat GP control/data as a GPR sequence */
42499860Sgdamore@opensolaris.org 			block->gprseqlen = 2;
42509860Sgdamore@opensolaris.org 			block->gprseq[0] = *vi++;
42519860Sgdamore@opensolaris.org 			block->gprseq[0] |= *vi++ << 8;
42529860Sgdamore@opensolaris.org 			block->gprseq[0] |= GPR_CONTROL_WRITE;
42539860Sgdamore@opensolaris.org 			block->gprseq[1] = *vi++;
42549860Sgdamore@opensolaris.org 			block->gprseq[1] |= *vi++ << 8;
42559860Sgdamore@opensolaris.org 			break;
42569860Sgdamore@opensolaris.org 
42579860Sgdamore@opensolaris.org 		case 3: /* MII/PHY : Section 4.4.2.1.2 */
42589860Sgdamore@opensolaris.org 			block->command = CMD_PS;
42599860Sgdamore@opensolaris.org 			block->media_code = MEDIA_MII;
42609860Sgdamore@opensolaris.org 			block->un.mii.phy_num = *vi++;
42619860Sgdamore@opensolaris.org 
42629860Sgdamore@opensolaris.org 			block->gprseqlen = *vi++;
42639860Sgdamore@opensolaris.org 			for (i = 0; i < block->gprseqlen; i++) {
42649860Sgdamore@opensolaris.org 				block->gprseq[i] = *vi++;
42659860Sgdamore@opensolaris.org 				block->gprseq[i] |= *vi++ << 8;
42669860Sgdamore@opensolaris.org 			}
42679860Sgdamore@opensolaris.org 
42689860Sgdamore@opensolaris.org 			block->rstseqlen = *vi++;
42699860Sgdamore@opensolaris.org 			for (i = 0; i < block->rstseqlen; i++) {
42709860Sgdamore@opensolaris.org 				block->rstseq[i] = *vi++;
42719860Sgdamore@opensolaris.org 				block->rstseq[i] |= *vi++ << 8;
42729860Sgdamore@opensolaris.org 			}
42739860Sgdamore@opensolaris.org 			block->un.mii.mediacaps = *vi++;
42749860Sgdamore@opensolaris.org 			block->un.mii.mediacaps |= *vi++ << 8;
42759860Sgdamore@opensolaris.org 			block->un.mii.nwayadvert = *vi++;
42769860Sgdamore@opensolaris.org 			block->un.mii.nwayadvert |= *vi++ << 8;
42779860Sgdamore@opensolaris.org 			block->un.mii.fdxmask = *vi++;
42789860Sgdamore@opensolaris.org 			block->un.mii.fdxmask |= *vi++ << 8;
42799860Sgdamore@opensolaris.org 			block->un.mii.ttmmask = *vi++;
42809860Sgdamore@opensolaris.org 			block->un.mii.ttmmask |= *vi++ << 8;
42819860Sgdamore@opensolaris.org 			block->un.mii.miiintr |= *vi++;
42829860Sgdamore@opensolaris.org 			break;
42839860Sgdamore@opensolaris.org 
42849860Sgdamore@opensolaris.org 		case 4: /* SYM Media: 4.5.2.1.3 */
42859860Sgdamore@opensolaris.org 			block->media_code = *vi++ & 0x3f;
42869860Sgdamore@opensolaris.org 			/* Treat GP control and data as a GPR sequence */
42879860Sgdamore@opensolaris.org 			block->gprseqlen = 2;
42889860Sgdamore@opensolaris.org 			block->gprseq[0] = *vi++;
42899860Sgdamore@opensolaris.org 			block->gprseq[0] |= *vi++ << 8;
42909860Sgdamore@opensolaris.org 			block->gprseq[0] |= GPR_CONTROL_WRITE;
42919860Sgdamore@opensolaris.org 			block->gprseq[1]  = *vi++;
42929860Sgdamore@opensolaris.org 			block->gprseq[1] |= *vi++ << 8;
42939860Sgdamore@opensolaris.org 			block->command = *vi++;
42949860Sgdamore@opensolaris.org 			block->command |= *vi++ << 8;
42959860Sgdamore@opensolaris.org 			break;
42969860Sgdamore@opensolaris.org 
42979860Sgdamore@opensolaris.org 		case 5: /* GPR reset sequence:  Section 4.5.2.1.4 */
42989860Sgdamore@opensolaris.org 			block->rstseqlen = *vi++;
42999860Sgdamore@opensolaris.org 			for (i = 0; i < block->rstseqlen; i++)
43009860Sgdamore@opensolaris.org 				block->rstseq[i] = *vi++;
43019860Sgdamore@opensolaris.org 			break;
43029860Sgdamore@opensolaris.org 
43039860Sgdamore@opensolaris.org 		default: /* Unknown media block. Skip it. */
43049860Sgdamore@opensolaris.org 			cmn_err(CE_WARN, "dnet: Unsupported SROM block.");
43059860Sgdamore@opensolaris.org 			vi += blocklen;
43069860Sgdamore@opensolaris.org 			break;
43079860Sgdamore@opensolaris.org 		}
43089860Sgdamore@opensolaris.org 	} else { /* Compact format (or V1 SROM): Section 4.3.2.1 */
43099860Sgdamore@opensolaris.org 		block->type = 0;
43109860Sgdamore@opensolaris.org 		block->media_code = *vi++ & 0x3f;
43119860Sgdamore@opensolaris.org 		block->gprseqlen = 1;
43129860Sgdamore@opensolaris.org 		block->gprseq[0] = *vi++;
43139860Sgdamore@opensolaris.org 		block->command = *vi++;
43149860Sgdamore@opensolaris.org 		block->command |= (*vi++) << 8;
43159860Sgdamore@opensolaris.org 	}
43169860Sgdamore@opensolaris.org 	return (vi);
43179860Sgdamore@opensolaris.org }
43189860Sgdamore@opensolaris.org 
43199860Sgdamore@opensolaris.org 
43209860Sgdamore@opensolaris.org /*
43219860Sgdamore@opensolaris.org  * An alternative to doing this would be to store the legacy ROMs in binary
43229860Sgdamore@opensolaris.org  * format in the conf file, and in read_srom, pick out the data. This would
43239860Sgdamore@opensolaris.org  * then allow the parser to continue on as normal. This makes it a little
43249860Sgdamore@opensolaris.org  * easier to read.
43259860Sgdamore@opensolaris.org  */
43269860Sgdamore@opensolaris.org static void
setup_legacy_blocks()43279860Sgdamore@opensolaris.org setup_legacy_blocks()
43289860Sgdamore@opensolaris.org {
43299860Sgdamore@opensolaris.org 	LEAF_FORMAT *leaf;
43309860Sgdamore@opensolaris.org 	media_block_t *block;
43319860Sgdamore@opensolaris.org 
43329860Sgdamore@opensolaris.org 	/* Default FAKE SROM */
43339860Sgdamore@opensolaris.org 	leaf = &leaf_default_100;
43349860Sgdamore@opensolaris.org 	leaf->is_static = 1;
43359860Sgdamore@opensolaris.org 	leaf->default_block = &leaf->block[3];
43369860Sgdamore@opensolaris.org 	leaf->block_count = 4; /* 100 cards are highly unlikely to have BNC */
43379860Sgdamore@opensolaris.org 	block = leaf->block;
43389860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP_FD;
43399860Sgdamore@opensolaris.org 	block->type = 0;
43409860Sgdamore@opensolaris.org 	block->command = 0x8e;  /* PCS, PS off, media sense: bit7, pol=1 */
43419860Sgdamore@opensolaris.org 	block++;
43429860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP;
43439860Sgdamore@opensolaris.org 	block->type = 0;
43449860Sgdamore@opensolaris.org 	block->command = 0x8e;  /* PCS, PS off, media sense: bit7, pol=1 */
43459860Sgdamore@opensolaris.org 	block++;
43469860Sgdamore@opensolaris.org 	block->media_code = MEDIA_SYM_SCR_FD;
43479860Sgdamore@opensolaris.org 	block->type = 0;
43489860Sgdamore@opensolaris.org 	block->command = 0x6d;  /* PCS, PS, SCR on, media sense: bit6, pol=0 */
43499860Sgdamore@opensolaris.org 	block++;
43509860Sgdamore@opensolaris.org 	block->media_code = MEDIA_SYM_SCR;
43519860Sgdamore@opensolaris.org 	block->type = 0;
43529860Sgdamore@opensolaris.org 	block->command = 0x406d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
43539860Sgdamore@opensolaris.org 
43549860Sgdamore@opensolaris.org 	/* COGENT FAKE SROM */
43559860Sgdamore@opensolaris.org 	leaf = &leaf_cogent_100;
43569860Sgdamore@opensolaris.org 	leaf->is_static = 1;
43579860Sgdamore@opensolaris.org 	leaf->default_block = &leaf->block[4];
43589860Sgdamore@opensolaris.org 	leaf->block_count = 5; /* 100TX, 100TX-FD, 10T 10T-FD, BNC */
43599860Sgdamore@opensolaris.org 	block = leaf->block; /* BNC */
43609860Sgdamore@opensolaris.org 	block->media_code = MEDIA_BNC;
43619860Sgdamore@opensolaris.org 	block->type = 0;
43629860Sgdamore@opensolaris.org 	block->command =  0x8000; /* No media sense, PCS, SCR, PS all off */
43639860Sgdamore@opensolaris.org 	block->gprseqlen = 2;
43649860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
43659860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x13f;
43669860Sgdamore@opensolaris.org 	block->gprseq[1] = 1;
43679860Sgdamore@opensolaris.org 
43689860Sgdamore@opensolaris.org 	block++;
43699860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP_FD;
43709860Sgdamore@opensolaris.org 	block->type = 0;
43719860Sgdamore@opensolaris.org 	block->command = 0x8e;  /* PCS, PS off, media sense: bit7, pol=1 */
43729860Sgdamore@opensolaris.org 	block->gprseqlen = 2;
43739860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
43749860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x13f;
43759860Sgdamore@opensolaris.org 	block->gprseq[1] = 0x26;
43769860Sgdamore@opensolaris.org 
43779860Sgdamore@opensolaris.org 	block++; /* 10BaseT */
43789860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP;
43799860Sgdamore@opensolaris.org 	block->type = 0;
43809860Sgdamore@opensolaris.org 	block->command = 0x8e;  /* PCS, PS off, media sense: bit7, pol=1 */
43819860Sgdamore@opensolaris.org 	block->gprseqlen = 2;
43829860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
43839860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x13f;
43849860Sgdamore@opensolaris.org 	block->gprseq[1] = 0x3e;
43859860Sgdamore@opensolaris.org 
43869860Sgdamore@opensolaris.org 	block++; /* 100BaseTX-FD */
43879860Sgdamore@opensolaris.org 	block->media_code = MEDIA_SYM_SCR_FD;
43889860Sgdamore@opensolaris.org 	block->type = 0;
43899860Sgdamore@opensolaris.org 	block->command = 0x6d;  /* PCS, PS, SCR on, media sense: bit6, pol=0 */
43909860Sgdamore@opensolaris.org 	block->gprseqlen = 2;
43919860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
43929860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x13f;
43939860Sgdamore@opensolaris.org 	block->gprseq[1] = 1;
43949860Sgdamore@opensolaris.org 
43959860Sgdamore@opensolaris.org 	block++; /* 100BaseTX */
43969860Sgdamore@opensolaris.org 	block->media_code = MEDIA_SYM_SCR;
43979860Sgdamore@opensolaris.org 	block->type = 0;
43989860Sgdamore@opensolaris.org 	block->command = 0x406d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
43999860Sgdamore@opensolaris.org 	block->gprseqlen = 2;
44009860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
44019860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x13f;
44029860Sgdamore@opensolaris.org 	block->gprseq[1] = 1;
44039860Sgdamore@opensolaris.org 
44049860Sgdamore@opensolaris.org 	/* Generic legacy card with a PHY. */
44059860Sgdamore@opensolaris.org 	leaf = &leaf_phylegacy;
44069860Sgdamore@opensolaris.org 	leaf->block_count = 1;
44079860Sgdamore@opensolaris.org 	leaf->mii_block = leaf->block;
44089860Sgdamore@opensolaris.org 	leaf->default_block = &leaf->block[0];
44099860Sgdamore@opensolaris.org 	leaf->is_static = 1;
44109860Sgdamore@opensolaris.org 	block = leaf->block;
44119860Sgdamore@opensolaris.org 	block->media_code = MEDIA_MII;
44129860Sgdamore@opensolaris.org 	block->type = 1; /* MII Block type 1 */
44139860Sgdamore@opensolaris.org 	block->command = 1; /* Port select */
44149860Sgdamore@opensolaris.org 	block->gprseqlen = 0;
44159860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
44169860Sgdamore@opensolaris.org 
44179860Sgdamore@opensolaris.org 	/* ASANTE FAKE SROM */
44189860Sgdamore@opensolaris.org 	leaf = &leaf_asante;
44199860Sgdamore@opensolaris.org 	leaf->is_static = 1;
44209860Sgdamore@opensolaris.org 	leaf->default_block = &leaf->block[0];
44219860Sgdamore@opensolaris.org 	leaf->block_count = 1;
44229860Sgdamore@opensolaris.org 	block = leaf->block;
44239860Sgdamore@opensolaris.org 	block->media_code = MEDIA_MII;
44249860Sgdamore@opensolaris.org 	block->type = 1; /* MII Block type 1 */
44259860Sgdamore@opensolaris.org 	block->command = 1; /* Port select */
44269860Sgdamore@opensolaris.org 	block->gprseqlen = 3;
44279860Sgdamore@opensolaris.org 	block->rstseqlen = 0;
44289860Sgdamore@opensolaris.org 	block->gprseq[0] = 0x180;
44299860Sgdamore@opensolaris.org 	block->gprseq[1] = 0x80;
44309860Sgdamore@opensolaris.org 	block->gprseq[2] = 0x0;
44319860Sgdamore@opensolaris.org 
44329860Sgdamore@opensolaris.org 	/* LEGACY 21041 card FAKE SROM */
44339860Sgdamore@opensolaris.org 	leaf = &leaf_21041;
44349860Sgdamore@opensolaris.org 	leaf->is_static = 1;
44359860Sgdamore@opensolaris.org 	leaf->block_count = 4;  /* SIA Blocks for TP, TPfd, BNC, AUI */
44369860Sgdamore@opensolaris.org 	leaf->default_block = &leaf->block[3];
44379860Sgdamore@opensolaris.org 
44389860Sgdamore@opensolaris.org 	block = leaf->block;
44399860Sgdamore@opensolaris.org 	block->media_code = MEDIA_AUI;
44409860Sgdamore@opensolaris.org 	block->type = 2;
44419860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0xef09;
44429860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x0705;
44439860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x000e;
44449860Sgdamore@opensolaris.org 
44459860Sgdamore@opensolaris.org 	block++;
44469860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP_FD;
44479860Sgdamore@opensolaris.org 	block->type = 2;
44489860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0xef01;
44499860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x7f3d;
44509860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0008;
44519860Sgdamore@opensolaris.org 
44529860Sgdamore@opensolaris.org 	block++;
44539860Sgdamore@opensolaris.org 	block->media_code = MEDIA_BNC;
44549860Sgdamore@opensolaris.org 	block->type = 2;
44559860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0xef09;
44569860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x0705;
44579860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0006;
44589860Sgdamore@opensolaris.org 
44599860Sgdamore@opensolaris.org 	block++;
44609860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP;
44619860Sgdamore@opensolaris.org 	block->type = 2;
44629860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0xef01;
44639860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x7f3f;
44649860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0008;
44659860Sgdamore@opensolaris.org 
44669860Sgdamore@opensolaris.org 	/* LEGACY 21040 card FAKE SROM */
44679860Sgdamore@opensolaris.org 	leaf = &leaf_21040;
44689860Sgdamore@opensolaris.org 	leaf->is_static = 1;
44699860Sgdamore@opensolaris.org 	leaf->block_count = 4;  /* SIA Blocks for TP, TPfd, BNC, AUI */
44709860Sgdamore@opensolaris.org 	block = leaf->block;
44719860Sgdamore@opensolaris.org 	block->media_code = MEDIA_AUI;
44729860Sgdamore@opensolaris.org 	block->type = 2;
44739860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0x8f09;
44749860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x0705;
44759860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x000e;
44769860Sgdamore@opensolaris.org 	block++;
44779860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP_FD;
44789860Sgdamore@opensolaris.org 	block->type = 2;
44799860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0x0f01;
44809860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x7f3d;
44819860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0008;
44829860Sgdamore@opensolaris.org 	block++;
44839860Sgdamore@opensolaris.org 	block->media_code = MEDIA_BNC;
44849860Sgdamore@opensolaris.org 	block->type = 2;
44859860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0xef09;
44869860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x0705;
44879860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0006;
44889860Sgdamore@opensolaris.org 	block++;
44899860Sgdamore@opensolaris.org 	block->media_code = MEDIA_TP;
44909860Sgdamore@opensolaris.org 	block->type = 2;
44919860Sgdamore@opensolaris.org 	block->un.sia.csr13 = 0x8f01;
44929860Sgdamore@opensolaris.org 	block->un.sia.csr14 = 0x7f3f;
44939860Sgdamore@opensolaris.org 	block->un.sia.csr15 = 0x0008;
44949860Sgdamore@opensolaris.org }
44959860Sgdamore@opensolaris.org 
44969860Sgdamore@opensolaris.org static void
dnet_print_srom(SROM_FORMAT * sr)44979860Sgdamore@opensolaris.org dnet_print_srom(SROM_FORMAT *sr)
44989860Sgdamore@opensolaris.org {
44999860Sgdamore@opensolaris.org 	int i;
45009860Sgdamore@opensolaris.org 	uchar_t *a = sr->netaddr;
45019860Sgdamore@opensolaris.org 	cmn_err(CE_NOTE, "SROM Dump: %d. ver %d, Num adapters %d,"
45029860Sgdamore@opensolaris.org 	    "Addr:%x:%x:%x:%x:%x:%x",
45039860Sgdamore@opensolaris.org 	    sr->init_from_srom, sr->version, sr->adapters,
45049860Sgdamore@opensolaris.org 	    a[0], a[1], a[2], a[3], a[4], a[5]);
45059860Sgdamore@opensolaris.org 
45069860Sgdamore@opensolaris.org 	for (i = 0; i < sr->adapters; i++)
45079860Sgdamore@opensolaris.org 		dnet_dump_leaf(sr->leaf+i);
45089860Sgdamore@opensolaris.org }
45099860Sgdamore@opensolaris.org 
45109860Sgdamore@opensolaris.org static void
dnet_dump_leaf(LEAF_FORMAT * leaf)45119860Sgdamore@opensolaris.org dnet_dump_leaf(LEAF_FORMAT *leaf)
45129860Sgdamore@opensolaris.org {
45139860Sgdamore@opensolaris.org 	int i;
45149860Sgdamore@opensolaris.org 	cmn_err(CE_NOTE, "Leaf: Device %d, block_count %d, gpr: %x",
45159860Sgdamore@opensolaris.org 	    leaf->device_number, leaf->block_count, leaf->gpr);
45169860Sgdamore@opensolaris.org 	for (i = 0; i < leaf->block_count; i++)
45179860Sgdamore@opensolaris.org 		dnet_dump_block(leaf->block+i);
45189860Sgdamore@opensolaris.org }
45199860Sgdamore@opensolaris.org 
45209860Sgdamore@opensolaris.org static void
dnet_dump_block(media_block_t * block)45219860Sgdamore@opensolaris.org dnet_dump_block(media_block_t *block)
45229860Sgdamore@opensolaris.org {
45239860Sgdamore@opensolaris.org 	cmn_err(CE_NOTE, "Block(%p): type %x, media %s, command: %x ",
45249860Sgdamore@opensolaris.org 	    (void *)block,
45259860Sgdamore@opensolaris.org 	    block->type, media_str[block->media_code], block->command);
45269860Sgdamore@opensolaris.org 	dnet_dumpbin("\tGPR Seq", (uchar_t *)block->gprseq, 2,
45279860Sgdamore@opensolaris.org 	    block->gprseqlen *2);
45289860Sgdamore@opensolaris.org 	dnet_dumpbin("\tGPR Reset", (uchar_t *)block->rstseq, 2,
45299860Sgdamore@opensolaris.org 	    block->rstseqlen *2);
45309860Sgdamore@opensolaris.org 	switch (block->type) {
45319860Sgdamore@opensolaris.org 	case 1: case 3:
45329860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "\tMII Info: phy %d, nway %x, fdx"
45339860Sgdamore@opensolaris.org 		    "%x, ttm %x, mediacap %x",
45349860Sgdamore@opensolaris.org 		    block->un.mii.phy_num, block->un.mii.nwayadvert,
45359860Sgdamore@opensolaris.org 		    block->un.mii.fdxmask, block->un.mii.ttmmask,
45369860Sgdamore@opensolaris.org 		    block->un.mii.mediacaps);
45379860Sgdamore@opensolaris.org 		break;
45389860Sgdamore@opensolaris.org 	case 2:
45399860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "\tSIA Regs: CSR13:%x, CSR14:%x, CSR15:%x",
45409860Sgdamore@opensolaris.org 		    block->un.sia.csr13, block->un.sia.csr14,
45419860Sgdamore@opensolaris.org 		    block->un.sia.csr15);
45429860Sgdamore@opensolaris.org 		break;
45439860Sgdamore@opensolaris.org 	}
45449860Sgdamore@opensolaris.org }
45459860Sgdamore@opensolaris.org 
45469860Sgdamore@opensolaris.org 
45479860Sgdamore@opensolaris.org /* Utility to print out binary info dumps. Handy for SROMs, etc */
45489860Sgdamore@opensolaris.org 
45499860Sgdamore@opensolaris.org static int
hexcode(unsigned val)45509860Sgdamore@opensolaris.org hexcode(unsigned val)
45519860Sgdamore@opensolaris.org {
45529860Sgdamore@opensolaris.org 	if (val <= 9)
45539860Sgdamore@opensolaris.org 		return (val +'0');
45549860Sgdamore@opensolaris.org 	if (val <= 15)
45559860Sgdamore@opensolaris.org 		return (val + 'a' - 10);
45569860Sgdamore@opensolaris.org 	return (-1);
45579860Sgdamore@opensolaris.org }
45589860Sgdamore@opensolaris.org 
45599860Sgdamore@opensolaris.org static void
dnet_dumpbin(char * msg,unsigned char * data,int size,int len)45609860Sgdamore@opensolaris.org dnet_dumpbin(char *msg, unsigned char *data, int size, int len)
45619860Sgdamore@opensolaris.org {
45629860Sgdamore@opensolaris.org 	char hex[128], *p = hex;
45639860Sgdamore@opensolaris.org 	char ascii[128], *q = ascii;
45649860Sgdamore@opensolaris.org 	int i, j;
45659860Sgdamore@opensolaris.org 
45669860Sgdamore@opensolaris.org 	if (!len)
45679860Sgdamore@opensolaris.org 		return;
45689860Sgdamore@opensolaris.org 
45699860Sgdamore@opensolaris.org 	for (i = 0; i < len; i += size) {
45709860Sgdamore@opensolaris.org 		for (j = size - 1; j >= 0; j--) { /* PORTABILITY: byte order */
45719860Sgdamore@opensolaris.org 			*p++ = hexcode(data[i+j] >> 4);
45729860Sgdamore@opensolaris.org 			*p++ = hexcode(data[i+j] & 0xf);
45739860Sgdamore@opensolaris.org 			*q++ = (data[i+j] < 32 || data[i+j] > 127) ?
45749860Sgdamore@opensolaris.org 			    '.' : data[i];
45759860Sgdamore@opensolaris.org 		}
45769860Sgdamore@opensolaris.org 		*p++ = ' ';
45779860Sgdamore@opensolaris.org 		if (q-ascii >= 8) {
45789860Sgdamore@opensolaris.org 			*p = *q = 0;
45799860Sgdamore@opensolaris.org 			cmn_err(CE_NOTE, "%s: %s\t%s", msg, hex, ascii);
45809860Sgdamore@opensolaris.org 			p = hex;
45819860Sgdamore@opensolaris.org 			q = ascii;
45829860Sgdamore@opensolaris.org 		}
45839860Sgdamore@opensolaris.org 	}
45849860Sgdamore@opensolaris.org 	if (p != hex) {
45859860Sgdamore@opensolaris.org 		while ((p - hex) < 8*3)
45869860Sgdamore@opensolaris.org 			*p++ = ' ';
45879860Sgdamore@opensolaris.org 		*p = *q = 0;
45889860Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "%s: %s\t%s", msg, hex, ascii);
45899860Sgdamore@opensolaris.org 	}
45909860Sgdamore@opensolaris.org }
45919860Sgdamore@opensolaris.org 
45929860Sgdamore@opensolaris.org #ifdef DNETDEBUG
45939860Sgdamore@opensolaris.org void
dnet_usectimeout(struct dnetinstance * dnetp,uint32_t usecs,int contin,timercb_t cback)45949860Sgdamore@opensolaris.org dnet_usectimeout(struct dnetinstance *dnetp, uint32_t usecs, int contin,
45959860Sgdamore@opensolaris.org     timercb_t cback)
45969860Sgdamore@opensolaris.org {
45979860Sgdamore@opensolaris.org 	mutex_enter(&dnetp->intrlock);
45989860Sgdamore@opensolaris.org 	dnetp->timer.start_ticks = (usecs * 100) / 8192;
45999860Sgdamore@opensolaris.org 	dnetp->timer.cb = cback;
46009860Sgdamore@opensolaris.org 	ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, GP_TIMER_REG),
46019860Sgdamore@opensolaris.org 	    dnetp->timer.start_ticks | (contin ? GPTIMER_CONT : 0));
46029860Sgdamore@opensolaris.org 	if (dnetp->timer.cb)
460310340Sstallion@opensolaris.org 		enable_interrupts(dnetp);
46049860Sgdamore@opensolaris.org 	mutex_exit(&dnetp->intrlock);
46059860Sgdamore@opensolaris.org }
46069860Sgdamore@opensolaris.org 
46079860Sgdamore@opensolaris.org uint32_t
dnet_usecelapsed(struct dnetinstance * dnetp)46089860Sgdamore@opensolaris.org dnet_usecelapsed(struct dnetinstance *dnetp)
46099860Sgdamore@opensolaris.org {
46109860Sgdamore@opensolaris.org 	uint32_t ticks = dnetp->timer.start_ticks -
46119860Sgdamore@opensolaris.org 	    (ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg, GP_TIMER_REG)) &
46129860Sgdamore@opensolaris.org 	    0xffff);
46139860Sgdamore@opensolaris.org 	return ((ticks * 8192) / 100);
46149860Sgdamore@opensolaris.org }
46159860Sgdamore@opensolaris.org 
46169860Sgdamore@opensolaris.org /* ARGSUSED */
46179860Sgdamore@opensolaris.org void
dnet_timestamp(struct dnetinstance * dnetp,char * buf)46189860Sgdamore@opensolaris.org dnet_timestamp(struct dnetinstance *dnetp,  char *buf)
46199860Sgdamore@opensolaris.org {
46209860Sgdamore@opensolaris.org 	uint32_t elapsed = dnet_usecelapsed(dnetp);
46219860Sgdamore@opensolaris.org 	char loc[32], *p = loc;
46229860Sgdamore@opensolaris.org 	int firstdigit = 1;
46239860Sgdamore@opensolaris.org 	uint32_t divisor;
46249860Sgdamore@opensolaris.org 
46259860Sgdamore@opensolaris.org 	while (*p++ = *buf++)
46269860Sgdamore@opensolaris.org 		;
46279860Sgdamore@opensolaris.org 	p--;
46289860Sgdamore@opensolaris.org 
46299860Sgdamore@opensolaris.org 	for (divisor = 1000000000; divisor /= 10; ) {
46309860Sgdamore@opensolaris.org 		int digit = (elapsed / divisor);
46319860Sgdamore@opensolaris.org 		elapsed -= digit * divisor;
46329860Sgdamore@opensolaris.org 		if (!firstdigit || digit) {
46339860Sgdamore@opensolaris.org 			*p++ = digit + '0';
46349860Sgdamore@opensolaris.org 			firstdigit = 0;
46359860Sgdamore@opensolaris.org 		}
46369860Sgdamore@opensolaris.org 
46379860Sgdamore@opensolaris.org 	}
46389860Sgdamore@opensolaris.org 
46399860Sgdamore@opensolaris.org 	/* Actual zero, output it */
46409860Sgdamore@opensolaris.org 	if (firstdigit)
46419860Sgdamore@opensolaris.org 		*p++ = '0';
46429860Sgdamore@opensolaris.org 
46439860Sgdamore@opensolaris.org 	*p++ = '-';
46449860Sgdamore@opensolaris.org 	*p++ = '>';
46459860Sgdamore@opensolaris.org 	*p++ = 0;
46469860Sgdamore@opensolaris.org 
46479860Sgdamore@opensolaris.org 	printf(loc);
46489860Sgdamore@opensolaris.org 	dnet_usectimeout(dnetp, 1000000, 0, 0);
46499860Sgdamore@opensolaris.org }
46509860Sgdamore@opensolaris.org 
46519860Sgdamore@opensolaris.org #endif
4652