xref: /onnv-gate/usr/src/uts/common/io/hme/hme.c (revision 12981:2ec828581df0)
19610Sgdamore@opensolaris.org /*
29610Sgdamore@opensolaris.org  * CDDL HEADER START
39610Sgdamore@opensolaris.org  *
49610Sgdamore@opensolaris.org  * The contents of this file are subject to the terms of the
59610Sgdamore@opensolaris.org  * Common Development and Distribution License (the "License").
69610Sgdamore@opensolaris.org  * You may not use this file except in compliance with the License.
79610Sgdamore@opensolaris.org  *
89610Sgdamore@opensolaris.org  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99610Sgdamore@opensolaris.org  * or http://www.opensolaris.org/os/licensing.
109610Sgdamore@opensolaris.org  * See the License for the specific language governing permissions
119610Sgdamore@opensolaris.org  * and limitations under the License.
129610Sgdamore@opensolaris.org  *
139610Sgdamore@opensolaris.org  * When distributing Covered Code, include this CDDL HEADER in each
149610Sgdamore@opensolaris.org  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159610Sgdamore@opensolaris.org  * If applicable, add the following below this CDDL HEADER, with the
169610Sgdamore@opensolaris.org  * fields enclosed by brackets "[]" replaced with your own identifying
179610Sgdamore@opensolaris.org  * information: Portions Copyright [yyyy] [name of copyright owner]
189610Sgdamore@opensolaris.org  *
199610Sgdamore@opensolaris.org  * CDDL HEADER END
209610Sgdamore@opensolaris.org  */
219610Sgdamore@opensolaris.org /*
22*12981SZeeshanul.Huq@Sun.COM  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
239610Sgdamore@opensolaris.org  */
249610Sgdamore@opensolaris.org 
259610Sgdamore@opensolaris.org 
269610Sgdamore@opensolaris.org /*
279610Sgdamore@opensolaris.org  * SunOS MT STREAMS FEPS(SBus)/Cheerio(PCI) 10/100Mb Ethernet Device Driver
289610Sgdamore@opensolaris.org  */
299610Sgdamore@opensolaris.org 
309610Sgdamore@opensolaris.org #include	<sys/types.h>
319610Sgdamore@opensolaris.org #include	<sys/debug.h>
329610Sgdamore@opensolaris.org #include	<sys/stream.h>
339610Sgdamore@opensolaris.org #include	<sys/cmn_err.h>
349610Sgdamore@opensolaris.org #include	<sys/kmem.h>
359610Sgdamore@opensolaris.org #include	<sys/crc32.h>
369610Sgdamore@opensolaris.org #include	<sys/modctl.h>
379610Sgdamore@opensolaris.org #include	<sys/conf.h>
389610Sgdamore@opensolaris.org #include	<sys/strsun.h>
399610Sgdamore@opensolaris.org #include	<sys/kstat.h>
409610Sgdamore@opensolaris.org #include	<sys/pattr.h>
419610Sgdamore@opensolaris.org #include	<sys/dlpi.h>
429610Sgdamore@opensolaris.org #include	<sys/strsubr.h>
439610Sgdamore@opensolaris.org #include	<sys/mac_provider.h>
449610Sgdamore@opensolaris.org #include	<sys/mac_ether.h>
4510806Sgdamore@opensolaris.org #include	<sys/mii.h>
469610Sgdamore@opensolaris.org #include	<sys/ethernet.h>
479610Sgdamore@opensolaris.org #include	<sys/vlan.h>
489610Sgdamore@opensolaris.org #include	<sys/pci.h>
499610Sgdamore@opensolaris.org #include	<sys/policy.h>
509610Sgdamore@opensolaris.org #include	<sys/ddi.h>
519610Sgdamore@opensolaris.org #include	<sys/sunddi.h>
5211878SVenu.Iyer@Sun.COM #include	<sys/byteorder.h>
539610Sgdamore@opensolaris.org #include	"hme_phy.h"
549610Sgdamore@opensolaris.org #include	"hme_mac.h"
559610Sgdamore@opensolaris.org #include	"hme.h"
569610Sgdamore@opensolaris.org 
579610Sgdamore@opensolaris.org typedef void	(*fptrv_t)();
589610Sgdamore@opensolaris.org 
599610Sgdamore@opensolaris.org typedef enum {
609610Sgdamore@opensolaris.org 	NO_MSG		= 0,
6110806Sgdamore@opensolaris.org 	AUTOCONFIG_MSG,
6210806Sgdamore@opensolaris.org 	DISPLAY_MSG,
6310806Sgdamore@opensolaris.org 	INIT_MSG,
6410806Sgdamore@opensolaris.org 	UNINIT_MSG,
6510806Sgdamore@opensolaris.org 	CONFIG_MSG,
6610806Sgdamore@opensolaris.org 	MII_MSG,
6710806Sgdamore@opensolaris.org 	FATAL_ERR_MSG,
6810806Sgdamore@opensolaris.org 	NFATAL_ERR_MSG,
6910806Sgdamore@opensolaris.org 	XCVR_MSG,
7010806Sgdamore@opensolaris.org 	NOXCVR_MSG,
7110806Sgdamore@opensolaris.org 	ERX_MSG,
7210806Sgdamore@opensolaris.org 	DDI_MSG,
739610Sgdamore@opensolaris.org } msg_t;
749610Sgdamore@opensolaris.org 
759610Sgdamore@opensolaris.org msg_t	hme_debug_level =	NO_MSG;
769610Sgdamore@opensolaris.org 
779610Sgdamore@opensolaris.org static char	*msg_string[] = {
789610Sgdamore@opensolaris.org 	"NONE       ",
799610Sgdamore@opensolaris.org 	"AUTOCONFIG ",
8010806Sgdamore@opensolaris.org 	"DISPLAY	"
819610Sgdamore@opensolaris.org 	"INIT       ",
829610Sgdamore@opensolaris.org 	"UNINIT		",
839610Sgdamore@opensolaris.org 	"CONFIG	",
8410806Sgdamore@opensolaris.org 	"MII	",
859610Sgdamore@opensolaris.org 	"FATAL_ERR	",
869610Sgdamore@opensolaris.org 	"NFATAL_ERR	",
879610Sgdamore@opensolaris.org 	"XCVR	",
889610Sgdamore@opensolaris.org 	"NOXCVR	",
899610Sgdamore@opensolaris.org 	"ERX	",
909610Sgdamore@opensolaris.org 	"DDI	",
919610Sgdamore@opensolaris.org };
929610Sgdamore@opensolaris.org 
939610Sgdamore@opensolaris.org #define	SEVERITY_NONE	0
949610Sgdamore@opensolaris.org #define	SEVERITY_LOW	0
959610Sgdamore@opensolaris.org #define	SEVERITY_MID	1
969610Sgdamore@opensolaris.org #define	SEVERITY_HIGH	2
979610Sgdamore@opensolaris.org #define	SEVERITY_UNKNOWN 99
989610Sgdamore@opensolaris.org 
999610Sgdamore@opensolaris.org #define	FEPS_URUN_BUG
1009610Sgdamore@opensolaris.org #define	HME_CODEVIOL_BUG
1019610Sgdamore@opensolaris.org 
1029610Sgdamore@opensolaris.org #define	KIOIP	KSTAT_INTR_PTR(hmep->hme_intrstats)
1039610Sgdamore@opensolaris.org 
1049610Sgdamore@opensolaris.org /*
1059610Sgdamore@opensolaris.org  * The following variables are used for checking fixes in Sbus/FEPS 2.0
1069610Sgdamore@opensolaris.org  */
1079610Sgdamore@opensolaris.org static	int	hme_urun_fix = 0;	/* Bug fixed in Sbus/FEPS 2.0 */
1089610Sgdamore@opensolaris.org 
1099610Sgdamore@opensolaris.org /*
1109610Sgdamore@opensolaris.org  * The following variables are used for configuring various features
1119610Sgdamore@opensolaris.org  */
1129610Sgdamore@opensolaris.org static	int	hme_64bit_enable =	1;	/* Use 64-bit sbus transfers */
1139610Sgdamore@opensolaris.org static	int	hme_reject_own =	1;	/* Reject packets with own SA */
11410806Sgdamore@opensolaris.org static	int	hme_ngu_enable =	0;	/* Never Give Up mode */
11510806Sgdamore@opensolaris.org 
11611878SVenu.Iyer@Sun.COM char *hme_priv_prop[] = {
11711878SVenu.Iyer@Sun.COM 	"_ipg0",
11811878SVenu.Iyer@Sun.COM 	"_ipg1",
11911878SVenu.Iyer@Sun.COM 	"_ipg2",
12011878SVenu.Iyer@Sun.COM 	"_lance_mode",
12111878SVenu.Iyer@Sun.COM 	NULL
12210806Sgdamore@opensolaris.org };
1239610Sgdamore@opensolaris.org 
1249610Sgdamore@opensolaris.org static	int	hme_lance_mode =	1;	/* to enable lance mode */
1259610Sgdamore@opensolaris.org static	int	hme_ipg0 =		16;
1269610Sgdamore@opensolaris.org static	int	hme_ipg1 =		8;
1279610Sgdamore@opensolaris.org static	int	hme_ipg2 =		4;
1289610Sgdamore@opensolaris.org 
1299610Sgdamore@opensolaris.org /*
1309610Sgdamore@opensolaris.org  * The following parameters may be configured by the user. If they are not
1319610Sgdamore@opensolaris.org  * configured by the user, the values will be based on the capabilities of
1329610Sgdamore@opensolaris.org  * the transceiver.
1339610Sgdamore@opensolaris.org  * The value "HME_NOTUSR" is ORed with the parameter value to indicate values
1349610Sgdamore@opensolaris.org  * which are NOT configured by the user.
1359610Sgdamore@opensolaris.org  */
1369610Sgdamore@opensolaris.org 
1379610Sgdamore@opensolaris.org #define	HME_NOTUSR	0x0f000000
1389610Sgdamore@opensolaris.org #define	HME_MASK_1BIT	0x1
1399610Sgdamore@opensolaris.org #define	HME_MASK_5BIT	0x1f
1409610Sgdamore@opensolaris.org #define	HME_MASK_8BIT	0xff
1419610Sgdamore@opensolaris.org 
1429610Sgdamore@opensolaris.org /*
1439610Sgdamore@opensolaris.org  * All strings used by hme messaging functions
1449610Sgdamore@opensolaris.org  */
1459610Sgdamore@opensolaris.org 
1469610Sgdamore@opensolaris.org static	char *no_xcvr_msg =
1479610Sgdamore@opensolaris.org 	"No transceiver found.";
1489610Sgdamore@opensolaris.org 
1499610Sgdamore@opensolaris.org static	char *burst_size_msg =
1509610Sgdamore@opensolaris.org 	"Could not identify the burst size";
1519610Sgdamore@opensolaris.org 
1529610Sgdamore@opensolaris.org static	char *unk_rx_ringsz_msg =
1539610Sgdamore@opensolaris.org 	"Unknown receive RINGSZ";
1549610Sgdamore@opensolaris.org 
1559610Sgdamore@opensolaris.org static  char *add_intr_fail_msg =
1569610Sgdamore@opensolaris.org 	"ddi_add_intr(9F) failed";
1579610Sgdamore@opensolaris.org 
1589610Sgdamore@opensolaris.org static  char *mregs_4global_reg_fail_msg =
1599610Sgdamore@opensolaris.org 	"ddi_regs_map_setup(9F) for global reg failed";
1609610Sgdamore@opensolaris.org 
1619610Sgdamore@opensolaris.org static	char *mregs_4etx_reg_fail_msg =
1629610Sgdamore@opensolaris.org 	"ddi_map_regs for etx reg failed";
1639610Sgdamore@opensolaris.org 
1649610Sgdamore@opensolaris.org static	char *mregs_4erx_reg_fail_msg =
1659610Sgdamore@opensolaris.org 	"ddi_map_regs for erx reg failed";
1669610Sgdamore@opensolaris.org 
1679610Sgdamore@opensolaris.org static	char *mregs_4bmac_reg_fail_msg =
1689610Sgdamore@opensolaris.org 	"ddi_map_regs for bmac reg failed";
1699610Sgdamore@opensolaris.org 
1709610Sgdamore@opensolaris.org static	char *mregs_4mif_reg_fail_msg =
1719610Sgdamore@opensolaris.org 	"ddi_map_regs for mif reg failed";
1729610Sgdamore@opensolaris.org 
1739610Sgdamore@opensolaris.org static	char *init_fail_gen_msg =
1749610Sgdamore@opensolaris.org 	"Failed to initialize hardware/driver";
1759610Sgdamore@opensolaris.org 
1769610Sgdamore@opensolaris.org static	char *ddi_nregs_fail_msg =
1779610Sgdamore@opensolaris.org 	"ddi_dev_nregs failed(9F), returned %d";
1789610Sgdamore@opensolaris.org 
1799610Sgdamore@opensolaris.org static	char *bad_num_regs_msg =
1809610Sgdamore@opensolaris.org 	"Invalid number of registers.";
1819610Sgdamore@opensolaris.org 
1829610Sgdamore@opensolaris.org 
1839610Sgdamore@opensolaris.org /* FATAL ERR msgs */
1849610Sgdamore@opensolaris.org /*
1859610Sgdamore@opensolaris.org  * Function prototypes.
1869610Sgdamore@opensolaris.org  */
1879610Sgdamore@opensolaris.org /* these two are global so that qfe can use them */
1889610Sgdamore@opensolaris.org int hmeattach(dev_info_t *, ddi_attach_cmd_t);
1899610Sgdamore@opensolaris.org int hmedetach(dev_info_t *, ddi_detach_cmd_t);
1909610Sgdamore@opensolaris.org int hmequiesce(dev_info_t *);
1919610Sgdamore@opensolaris.org static	boolean_t hmeinit_xfer_params(struct hme *);
1929610Sgdamore@opensolaris.org static	uint_t hmestop(struct hme *);
1939610Sgdamore@opensolaris.org static	void hmestatinit(struct hme *);
1949610Sgdamore@opensolaris.org static	int hmeallocthings(struct hme *);
1959610Sgdamore@opensolaris.org static	void hmefreethings(struct hme *);
1969610Sgdamore@opensolaris.org static	int hmeallocbuf(struct hme *, hmebuf_t *, int);
1979610Sgdamore@opensolaris.org static	int hmeallocbufs(struct hme *);
1989610Sgdamore@opensolaris.org static	void hmefreebufs(struct hme *);
1999610Sgdamore@opensolaris.org static	void hmeget_hm_rev_property(struct hme *);
2009610Sgdamore@opensolaris.org static	boolean_t hmestart(struct hme *, mblk_t *);
2019610Sgdamore@opensolaris.org static	uint_t hmeintr(caddr_t);
2029610Sgdamore@opensolaris.org static	void hmereclaim(struct hme *);
2039610Sgdamore@opensolaris.org static	int hmeinit(struct hme *);
2049610Sgdamore@opensolaris.org static	void hmeuninit(struct hme *hmep);
2059610Sgdamore@opensolaris.org static 	mblk_t *hmeread(struct hme *, hmebuf_t *, uint32_t);
2069610Sgdamore@opensolaris.org static	void hmesavecntrs(struct hme *);
2079610Sgdamore@opensolaris.org static	void hme_fatal_err(struct hme *, uint_t);
2089610Sgdamore@opensolaris.org static	void hme_nonfatal_err(struct hme *, uint_t);
2099610Sgdamore@opensolaris.org static	int hmeburstsizes(struct hme *);
21010806Sgdamore@opensolaris.org static	void send_bit(struct hme *, uint16_t);
21110806Sgdamore@opensolaris.org static	uint16_t get_bit_std(uint8_t, struct hme *);
21210806Sgdamore@opensolaris.org static	uint16_t hme_bb_mii_read(struct hme *, uint8_t, uint8_t);
21310806Sgdamore@opensolaris.org static	void hme_bb_mii_write(struct hme *, uint8_t, uint8_t, uint16_t);
2149610Sgdamore@opensolaris.org static	void hme_bb_force_idle(struct hme *);
21510806Sgdamore@opensolaris.org static	uint16_t hme_mii_read(void *, uint8_t, uint8_t);
21610806Sgdamore@opensolaris.org static	void hme_mii_write(void *, uint8_t, uint8_t, uint16_t);
2179610Sgdamore@opensolaris.org static	void hme_setup_mac_address(struct hme *, dev_info_t *);
21810806Sgdamore@opensolaris.org static	void hme_mii_notify(void *, link_state_t);
2199610Sgdamore@opensolaris.org 
2209610Sgdamore@opensolaris.org static void hme_fault_msg(struct hme *, uint_t, msg_t, char *, ...);
2219610Sgdamore@opensolaris.org 
2229610Sgdamore@opensolaris.org static void hme_check_acc_handle(char *, uint_t, struct hme *,
2239610Sgdamore@opensolaris.org     ddi_acc_handle_t);
2249610Sgdamore@opensolaris.org 
2259610Sgdamore@opensolaris.org /*
2269610Sgdamore@opensolaris.org  * Nemo (GLDv3) Functions.
2279610Sgdamore@opensolaris.org  */
2289610Sgdamore@opensolaris.org static int	hme_m_stat(void *, uint_t, uint64_t *);
2299610Sgdamore@opensolaris.org static int	hme_m_start(void *);
2309610Sgdamore@opensolaris.org static void	hme_m_stop(void *);
2319610Sgdamore@opensolaris.org static int	hme_m_promisc(void *, boolean_t);
2329610Sgdamore@opensolaris.org static int	hme_m_multicst(void *, boolean_t, const uint8_t *);
2339610Sgdamore@opensolaris.org static int	hme_m_unicst(void *, const uint8_t *);
2349610Sgdamore@opensolaris.org static mblk_t	*hme_m_tx(void *, mblk_t *);
2359610Sgdamore@opensolaris.org static boolean_t	hme_m_getcapab(void *, mac_capab_t, void *);
23611878SVenu.Iyer@Sun.COM static int hme_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
23711878SVenu.Iyer@Sun.COM static void hme_m_propinfo(void *, const char *, mac_prop_id_t,
23811878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
23910806Sgdamore@opensolaris.org static int hme_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
24010806Sgdamore@opensolaris.org     const void *);
24110806Sgdamore@opensolaris.org 
24210806Sgdamore@opensolaris.org static mii_ops_t hme_mii_ops = {
24310806Sgdamore@opensolaris.org 	MII_OPS_VERSION,
24410806Sgdamore@opensolaris.org 	hme_mii_read,
24510806Sgdamore@opensolaris.org 	hme_mii_write,
24610806Sgdamore@opensolaris.org 	hme_mii_notify,
24710806Sgdamore@opensolaris.org 	NULL
24810806Sgdamore@opensolaris.org };
2499610Sgdamore@opensolaris.org 
2509610Sgdamore@opensolaris.org static mac_callbacks_t hme_m_callbacks = {
25111878SVenu.Iyer@Sun.COM 	MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2529610Sgdamore@opensolaris.org 	hme_m_stat,
2539610Sgdamore@opensolaris.org 	hme_m_start,
2549610Sgdamore@opensolaris.org 	hme_m_stop,
2559610Sgdamore@opensolaris.org 	hme_m_promisc,
2569610Sgdamore@opensolaris.org 	hme_m_multicst,
2579610Sgdamore@opensolaris.org 	hme_m_unicst,
2589610Sgdamore@opensolaris.org 	hme_m_tx,
25910806Sgdamore@opensolaris.org 	NULL,
26011878SVenu.Iyer@Sun.COM 	NULL,
2619610Sgdamore@opensolaris.org 	hme_m_getcapab,
26210806Sgdamore@opensolaris.org 	NULL,
26310806Sgdamore@opensolaris.org 	NULL,
26410806Sgdamore@opensolaris.org 	hme_m_setprop,
26510806Sgdamore@opensolaris.org 	hme_m_getprop,
26611878SVenu.Iyer@Sun.COM 	hme_m_propinfo
2679610Sgdamore@opensolaris.org };
2689610Sgdamore@opensolaris.org 
2699610Sgdamore@opensolaris.org DDI_DEFINE_STREAM_OPS(hme_dev_ops, nulldev, nulldev, hmeattach, hmedetach,
2709610Sgdamore@opensolaris.org     nodev, NULL, D_MP, NULL, hmequiesce);
2719610Sgdamore@opensolaris.org 
2729610Sgdamore@opensolaris.org #define	HME_FAULT_MSG1(p, s, t, f) \
2739610Sgdamore@opensolaris.org     hme_fault_msg((p), (s), (t), (f));
2749610Sgdamore@opensolaris.org 
2759610Sgdamore@opensolaris.org #define	HME_FAULT_MSG2(p, s, t, f, a) \
2769610Sgdamore@opensolaris.org     hme_fault_msg((p), (s), (t), (f), (a));
2779610Sgdamore@opensolaris.org 
2789610Sgdamore@opensolaris.org #define	HME_FAULT_MSG3(p, s, t, f, a, b) \
2799610Sgdamore@opensolaris.org     hme_fault_msg((p), (s), (t), (f), (a), (b));
2809610Sgdamore@opensolaris.org 
2819610Sgdamore@opensolaris.org #define	HME_FAULT_MSG4(p, s, t, f, a, b, c) \
2829610Sgdamore@opensolaris.org     hme_fault_msg((p), (s), (t), (f), (a), (b), (c));
2839610Sgdamore@opensolaris.org 
2849610Sgdamore@opensolaris.org #define	CHECK_MIFREG() \
2859610Sgdamore@opensolaris.org 	hme_check_acc_handle(__FILE__, __LINE__, hmep, hmep->hme_mifregh)
2869610Sgdamore@opensolaris.org #define	CHECK_ETXREG() \
2879610Sgdamore@opensolaris.org 	hme_check_acc_handle(__FILE__, __LINE__, hmep, hmep->hme_etxregh)
2889610Sgdamore@opensolaris.org #define	CHECK_ERXREG() \
2899610Sgdamore@opensolaris.org 	hme_check_acc_handle(__FILE__, __LINE__, hmep, hmep->hme_erxregh)
2909610Sgdamore@opensolaris.org #define	CHECK_MACREG() \
2919610Sgdamore@opensolaris.org 	hme_check_acc_handle(__FILE__, __LINE__, hmep, hmep->hme_bmacregh)
2929610Sgdamore@opensolaris.org #define	CHECK_GLOBREG() \
2939610Sgdamore@opensolaris.org 	hme_check_acc_handle(__FILE__, __LINE__, hmep, hmep->hme_globregh)
2949610Sgdamore@opensolaris.org 
2959610Sgdamore@opensolaris.org /*
2969610Sgdamore@opensolaris.org  * Claim the device is ultra-capable of burst in the beginning.  Use
2979610Sgdamore@opensolaris.org  * the value returned by ddi_dma_burstsizes() to actually set the HME
2989610Sgdamore@opensolaris.org  * global configuration register later.
2999610Sgdamore@opensolaris.org  *
3009610Sgdamore@opensolaris.org  * Sbus/FEPS supports burst sizes of 16, 32 and 64 bytes. Also, it supports
3019610Sgdamore@opensolaris.org  * 32-bit and 64-bit Sbus transfers. Hence the dlim_burstsizes field contains
3029610Sgdamore@opensolaris.org  * the the burstsizes in both the lo and hi words.
3039610Sgdamore@opensolaris.org  */
3049610Sgdamore@opensolaris.org #define	HMELIMADDRLO	((uint64_t)0x00000000)
3059610Sgdamore@opensolaris.org #define	HMELIMADDRHI	((uint64_t)0xffffffff)
3069610Sgdamore@opensolaris.org 
3079610Sgdamore@opensolaris.org /*
3089610Sgdamore@opensolaris.org  * Note that rx and tx data buffers can be arbitrarily aligned, but
3099610Sgdamore@opensolaris.org  * that the descriptor rings need to be aligned on 2K boundaries, per
3109610Sgdamore@opensolaris.org  * the spec.
3119610Sgdamore@opensolaris.org  */
3129610Sgdamore@opensolaris.org static ddi_dma_attr_t hme_dma_attr = {
3139610Sgdamore@opensolaris.org 	DMA_ATTR_V0,		/* version number. */
3149610Sgdamore@opensolaris.org 	(uint64_t)HMELIMADDRLO,	/* low address */
3159610Sgdamore@opensolaris.org 	(uint64_t)HMELIMADDRHI,	/* high address */
3169610Sgdamore@opensolaris.org 	(uint64_t)0x00ffffff,	/* address counter max */
3179610Sgdamore@opensolaris.org 	(uint64_t)HME_HMDALIGN,	/* alignment */
3189610Sgdamore@opensolaris.org 	(uint_t)0x00700070,	/* dlim_burstsizes for 32 and 64 bit xfers */
3199610Sgdamore@opensolaris.org 	(uint32_t)0x1,		/* minimum transfer size */
3209610Sgdamore@opensolaris.org 	(uint64_t)0x7fffffff,	/* maximum transfer size */
3219610Sgdamore@opensolaris.org 	(uint64_t)0x00ffffff,	/* maximum segment size */
3229610Sgdamore@opensolaris.org 	1,			/* scatter/gather list length */
3239610Sgdamore@opensolaris.org 	512,			/* granularity */
3249610Sgdamore@opensolaris.org 	0			/* attribute flags */
3259610Sgdamore@opensolaris.org };
3269610Sgdamore@opensolaris.org 
3279610Sgdamore@opensolaris.org static ddi_device_acc_attr_t hme_buf_attr = {
3289610Sgdamore@opensolaris.org 	DDI_DEVICE_ATTR_V0,
3299610Sgdamore@opensolaris.org 	DDI_NEVERSWAP_ACC,
3309610Sgdamore@opensolaris.org 	DDI_STRICTORDER_ACC,	/* probably could allow merging & caching */
3319610Sgdamore@opensolaris.org 	DDI_DEFAULT_ACC,
3329610Sgdamore@opensolaris.org };
3339610Sgdamore@opensolaris.org 
3349610Sgdamore@opensolaris.org static uchar_t pci_latency_timer = 0;
3359610Sgdamore@opensolaris.org 
3369610Sgdamore@opensolaris.org /*
3379610Sgdamore@opensolaris.org  * Module linkage information for the kernel.
3389610Sgdamore@opensolaris.org  */
3399610Sgdamore@opensolaris.org static struct modldrv modldrv = {
3409610Sgdamore@opensolaris.org 	&mod_driverops,	/* Type of module.  This one is a driver */
3419610Sgdamore@opensolaris.org 	"Sun HME 10/100 Mb Ethernet",
3429610Sgdamore@opensolaris.org 	&hme_dev_ops,	/* driver ops */
3439610Sgdamore@opensolaris.org };
3449610Sgdamore@opensolaris.org 
3459610Sgdamore@opensolaris.org static struct modlinkage modlinkage = {
3469610Sgdamore@opensolaris.org 	MODREV_1, &modldrv, NULL
3479610Sgdamore@opensolaris.org };
3489610Sgdamore@opensolaris.org 
3499610Sgdamore@opensolaris.org /* <<<<<<<<<<<<<<<<<<<<<<  Register operations >>>>>>>>>>>>>>>>>>>>> */
3509610Sgdamore@opensolaris.org 
3519610Sgdamore@opensolaris.org #define	GET_MIFREG(reg) \
3529610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_mifregh, (uint32_t *)&hmep->hme_mifregp->reg)
3539610Sgdamore@opensolaris.org #define	PUT_MIFREG(reg, value) \
3549610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_mifregh, (uint32_t *)&hmep->hme_mifregp->reg, value)
3559610Sgdamore@opensolaris.org 
3569610Sgdamore@opensolaris.org #define	GET_ETXREG(reg) \
3579610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_etxregh, (uint32_t *)&hmep->hme_etxregp->reg)
3589610Sgdamore@opensolaris.org #define	PUT_ETXREG(reg, value) \
3599610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_etxregh, (uint32_t *)&hmep->hme_etxregp->reg, value)
3609610Sgdamore@opensolaris.org #define	GET_ERXREG(reg) \
3619610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_erxregh, (uint32_t *)&hmep->hme_erxregp->reg)
3629610Sgdamore@opensolaris.org #define	PUT_ERXREG(reg, value) \
3639610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_erxregh, (uint32_t *)&hmep->hme_erxregp->reg, value)
3649610Sgdamore@opensolaris.org #define	GET_MACREG(reg) \
3659610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_bmacregh, (uint32_t *)&hmep->hme_bmacregp->reg)
3669610Sgdamore@opensolaris.org #define	PUT_MACREG(reg, value) \
3679610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_bmacregh, \
3689610Sgdamore@opensolaris.org 		(uint32_t *)&hmep->hme_bmacregp->reg, value)
3699610Sgdamore@opensolaris.org #define	GET_GLOBREG(reg) \
3709610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_globregh, (uint32_t *)&hmep->hme_globregp->reg)
3719610Sgdamore@opensolaris.org #define	PUT_GLOBREG(reg, value) \
3729610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_globregh, \
3739610Sgdamore@opensolaris.org 		(uint32_t *)&hmep->hme_globregp->reg, value)
3749610Sgdamore@opensolaris.org #define	PUT_TMD(ptr, paddr, len, flags)					\
3759610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_tmd_acch, &hmep->hme_tmdp[ptr].tmd_addr, paddr); \
3769610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_tmd_acch, &hmep->hme_tmdp[ptr].tmd_flags,	\
3779610Sgdamore@opensolaris.org 	    len | flags)
3789610Sgdamore@opensolaris.org #define	GET_TMD_FLAGS(ptr)					\
3799610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_tmd_acch, &hmep->hme_tmdp[ptr].tmd_flags)
3809610Sgdamore@opensolaris.org #define	PUT_RMD(ptr, paddr) \
3819610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_rmd_acch, &hmep->hme_rmdp[ptr].rmd_addr, paddr); \
3829610Sgdamore@opensolaris.org 	ddi_put32(hmep->hme_rmd_acch, &hmep->hme_rmdp[ptr].rmd_flags,	\
3839610Sgdamore@opensolaris.org 	    (uint32_t)(HMEBUFSIZE << HMERMD_BUFSIZE_SHIFT) | HMERMD_OWN)
3849610Sgdamore@opensolaris.org #define	GET_RMD_FLAGS(ptr)					\
3859610Sgdamore@opensolaris.org 	ddi_get32(hmep->hme_rmd_acch, &hmep->hme_rmdp[ptr].rmd_flags)
3869610Sgdamore@opensolaris.org 
3879610Sgdamore@opensolaris.org #define	GET_ROM8(offset) \
3889610Sgdamore@opensolaris.org 	ddi_get8((hmep->hme_romh), (offset))
3899610Sgdamore@opensolaris.org 
3909610Sgdamore@opensolaris.org /*
3919610Sgdamore@opensolaris.org  * Ether_copy is not endian-correct. Define an endian-correct version.
3929610Sgdamore@opensolaris.org  */
3939610Sgdamore@opensolaris.org #define	ether_bcopy(a, b) (bcopy(a, b, 6))
3949610Sgdamore@opensolaris.org 
3959610Sgdamore@opensolaris.org /*
3969610Sgdamore@opensolaris.org  * Ether-type is specifically big-endian, but data region is unknown endian
3979610Sgdamore@opensolaris.org  */
3989610Sgdamore@opensolaris.org #define	get_ether_type(ptr) \
3999610Sgdamore@opensolaris.org 	(((((uint8_t *)ptr)[12] << 8) | (((uint8_t *)ptr)[13])))
4009610Sgdamore@opensolaris.org 
4019610Sgdamore@opensolaris.org /* <<<<<<<<<<<<<<<<<<<<<<  Configuration Parameters >>>>>>>>>>>>>>>>>>>>> */
4029610Sgdamore@opensolaris.org 
4039610Sgdamore@opensolaris.org #define	BMAC_DEFAULT_JAMSIZE	(0x04)		/* jamsize equals 4 */
4049610Sgdamore@opensolaris.org #define	BMAC_LONG_JAMSIZE	(0x10)		/* jamsize equals 0x10 */
4059610Sgdamore@opensolaris.org static	int 	jamsize = BMAC_DEFAULT_JAMSIZE;
4069610Sgdamore@opensolaris.org 
4079610Sgdamore@opensolaris.org 
4089610Sgdamore@opensolaris.org /*
4099610Sgdamore@opensolaris.org  * Calculate the bit in the multicast address filter that selects the given
4109610Sgdamore@opensolaris.org  * address.
4119610Sgdamore@opensolaris.org  */
4129610Sgdamore@opensolaris.org 
4139610Sgdamore@opensolaris.org static uint32_t
hmeladrf_bit(const uint8_t * addr)4149610Sgdamore@opensolaris.org hmeladrf_bit(const uint8_t *addr)
4159610Sgdamore@opensolaris.org {
4169610Sgdamore@opensolaris.org 	uint32_t crc;
4179610Sgdamore@opensolaris.org 
4189610Sgdamore@opensolaris.org 	CRC32(crc, addr, ETHERADDRL, -1U, crc32_table);
4199610Sgdamore@opensolaris.org 
4209610Sgdamore@opensolaris.org 	/*
4219610Sgdamore@opensolaris.org 	 * Just want the 6 most significant bits.
4229610Sgdamore@opensolaris.org 	 */
4239610Sgdamore@opensolaris.org 	return (crc >> 26);
4249610Sgdamore@opensolaris.org }
4259610Sgdamore@opensolaris.org 
4269610Sgdamore@opensolaris.org /* <<<<<<<<<<<<<<<<<<<<<<<<  Bit Bang Operations >>>>>>>>>>>>>>>>>>>>>>>> */
4279610Sgdamore@opensolaris.org 
4289610Sgdamore@opensolaris.org static void
send_bit(struct hme * hmep,uint16_t x)42910806Sgdamore@opensolaris.org send_bit(struct hme *hmep, uint16_t x)
4309610Sgdamore@opensolaris.org {
4319610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbdata, x);
4329610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbclk, HME_BBCLK_LOW);
4339610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbclk, HME_BBCLK_HIGH);
4349610Sgdamore@opensolaris.org }
4359610Sgdamore@opensolaris.org 
4369610Sgdamore@opensolaris.org 
4379610Sgdamore@opensolaris.org /*
4389610Sgdamore@opensolaris.org  * To read the MII register bits according to the IEEE Standard
4399610Sgdamore@opensolaris.org  */
44010806Sgdamore@opensolaris.org static uint16_t
get_bit_std(uint8_t phyad,struct hme * hmep)44110806Sgdamore@opensolaris.org get_bit_std(uint8_t phyad, struct hme *hmep)
4429610Sgdamore@opensolaris.org {
44310806Sgdamore@opensolaris.org 	uint16_t	x;
4449610Sgdamore@opensolaris.org 
4459610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbclk, HME_BBCLK_LOW);
4469610Sgdamore@opensolaris.org 	drv_usecwait(1);	/* wait for  >330 ns for stable data */
44710806Sgdamore@opensolaris.org 	if (phyad == HME_INTERNAL_PHYAD)
4489610Sgdamore@opensolaris.org 		x = (GET_MIFREG(mif_cfg) & HME_MIF_CFGM0) ? 1 : 0;
4499610Sgdamore@opensolaris.org 	else
4509610Sgdamore@opensolaris.org 		x = (GET_MIFREG(mif_cfg) & HME_MIF_CFGM1) ? 1 : 0;
4519610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbclk, HME_BBCLK_HIGH);
4529610Sgdamore@opensolaris.org 	return (x);
4539610Sgdamore@opensolaris.org }
4549610Sgdamore@opensolaris.org 
4559610Sgdamore@opensolaris.org #define	SEND_BIT(x)		send_bit(hmep, x)
45610806Sgdamore@opensolaris.org #define	GET_BIT_STD(phyad, x)	x = get_bit_std(phyad, hmep)
4579610Sgdamore@opensolaris.org 
4589610Sgdamore@opensolaris.org 
4599610Sgdamore@opensolaris.org static void
hme_bb_mii_write(struct hme * hmep,uint8_t phyad,uint8_t regad,uint16_t data)46010806Sgdamore@opensolaris.org hme_bb_mii_write(struct hme *hmep, uint8_t phyad, uint8_t regad, uint16_t data)
4619610Sgdamore@opensolaris.org {
4629610Sgdamore@opensolaris.org 	int	i;
4639610Sgdamore@opensolaris.org 
4649610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbopenb, 1);	/* Enable the MII driver */
4659610Sgdamore@opensolaris.org 	(void) hme_bb_force_idle(hmep);
4669610Sgdamore@opensolaris.org 	SEND_BIT(0); SEND_BIT(1);	/* <ST> */
4679610Sgdamore@opensolaris.org 	SEND_BIT(0); SEND_BIT(1);	/* <OP> */
4689610Sgdamore@opensolaris.org 
4699610Sgdamore@opensolaris.org 	for (i = 4; i >= 0; i--) {		/* <AAAAA> */
4709610Sgdamore@opensolaris.org 		SEND_BIT((phyad >> i) & 1);
4719610Sgdamore@opensolaris.org 	}
4729610Sgdamore@opensolaris.org 
4739610Sgdamore@opensolaris.org 	for (i = 4; i >= 0; i--) {		/* <RRRRR> */
4749610Sgdamore@opensolaris.org 		SEND_BIT((regad >> i) & 1);
4759610Sgdamore@opensolaris.org 	}
4769610Sgdamore@opensolaris.org 
4779610Sgdamore@opensolaris.org 	SEND_BIT(1); SEND_BIT(0);	/* <TA> */
4789610Sgdamore@opensolaris.org 
4799610Sgdamore@opensolaris.org 	for (i = 0xf; i >= 0; i--) {	/* <DDDDDDDDDDDDDDDD> */
4809610Sgdamore@opensolaris.org 		SEND_BIT((data >> i) & 1);
4819610Sgdamore@opensolaris.org 	}
4829610Sgdamore@opensolaris.org 
4839610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbopenb, 0);	/* Disable the MII driver */
4849610Sgdamore@opensolaris.org 	CHECK_MIFREG();
4859610Sgdamore@opensolaris.org }
4869610Sgdamore@opensolaris.org 
4879610Sgdamore@opensolaris.org /* Return 0 if OK, 1 if error (Transceiver does not talk management) */
48810806Sgdamore@opensolaris.org static uint16_t
hme_bb_mii_read(struct hme * hmep,uint8_t phyad,uint8_t regad)48910806Sgdamore@opensolaris.org hme_bb_mii_read(struct hme *hmep, uint8_t phyad, uint8_t regad)
4909610Sgdamore@opensolaris.org {
4919610Sgdamore@opensolaris.org 	int		i;
4929610Sgdamore@opensolaris.org 	uint32_t	x;
49310806Sgdamore@opensolaris.org 	uint16_t	data = 0;
4949610Sgdamore@opensolaris.org 
4959610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbopenb, 1);	/* Enable the MII driver */
4969610Sgdamore@opensolaris.org 	(void) hme_bb_force_idle(hmep);
4979610Sgdamore@opensolaris.org 	SEND_BIT(0); SEND_BIT(1);	/* <ST> */
4989610Sgdamore@opensolaris.org 	SEND_BIT(1); SEND_BIT(0);	/* <OP> */
4999610Sgdamore@opensolaris.org 	for (i = 4; i >= 0; i--) {		/* <AAAAA> */
5009610Sgdamore@opensolaris.org 		SEND_BIT((phyad >> i) & 1);
5019610Sgdamore@opensolaris.org 	}
5029610Sgdamore@opensolaris.org 	for (i = 4; i >= 0; i--) {		/* <RRRRR> */
5039610Sgdamore@opensolaris.org 		SEND_BIT((regad >> i) & 1);
5049610Sgdamore@opensolaris.org 	}
5059610Sgdamore@opensolaris.org 
5069610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_bbopenb, 0);	/* Disable the MII driver */
5079610Sgdamore@opensolaris.org 
50810806Sgdamore@opensolaris.org 	GET_BIT_STD(phyad, x);
50910806Sgdamore@opensolaris.org 	GET_BIT_STD(phyad, x);		/* <TA> */
51010806Sgdamore@opensolaris.org 	for (i = 0xf; i >= 0; i--) {	/* <DDDDDDDDDDDDDDDD> */
51110806Sgdamore@opensolaris.org 		GET_BIT_STD(phyad, x);
51210806Sgdamore@opensolaris.org 		data += (x << i);
5139610Sgdamore@opensolaris.org 	}
51410806Sgdamore@opensolaris.org 	/*
51510806Sgdamore@opensolaris.org 	 * Kludge to get the Transceiver out of hung mode
51610806Sgdamore@opensolaris.org 	 */
51710806Sgdamore@opensolaris.org 	GET_BIT_STD(phyad, x);
51810806Sgdamore@opensolaris.org 	GET_BIT_STD(phyad, x);
51910806Sgdamore@opensolaris.org 	GET_BIT_STD(phyad, x);
5209610Sgdamore@opensolaris.org 	CHECK_MIFREG();
52110806Sgdamore@opensolaris.org 	return (data);
5229610Sgdamore@opensolaris.org }
5239610Sgdamore@opensolaris.org 
5249610Sgdamore@opensolaris.org 
5259610Sgdamore@opensolaris.org static void
hme_bb_force_idle(struct hme * hmep)5269610Sgdamore@opensolaris.org hme_bb_force_idle(struct hme *hmep)
5279610Sgdamore@opensolaris.org {
5289610Sgdamore@opensolaris.org 	int	i;
5299610Sgdamore@opensolaris.org 
5309610Sgdamore@opensolaris.org 	for (i = 0; i < 33; i++) {
5319610Sgdamore@opensolaris.org 		SEND_BIT(1);
5329610Sgdamore@opensolaris.org 	}
5339610Sgdamore@opensolaris.org }
5349610Sgdamore@opensolaris.org 
5359610Sgdamore@opensolaris.org /* <<<<<<<<<<<<<<<<<<<<End of Bit Bang Operations >>>>>>>>>>>>>>>>>>>>>>>> */
5369610Sgdamore@opensolaris.org 
5379610Sgdamore@opensolaris.org 
5389610Sgdamore@opensolaris.org /* <<<<<<<<<<<<< Frame Register used for MII operations >>>>>>>>>>>>>>>>>>>> */
5399610Sgdamore@opensolaris.org 
5409610Sgdamore@opensolaris.org /* Return 0 if OK, 1 if error (Transceiver does not talk management) */
54110806Sgdamore@opensolaris.org static uint16_t
hme_mii_read(void * arg,uint8_t phyad,uint8_t regad)54210806Sgdamore@opensolaris.org hme_mii_read(void *arg, uint8_t phyad, uint8_t regad)
5439610Sgdamore@opensolaris.org {
54410806Sgdamore@opensolaris.org 	struct hme	*hmep = arg;
5459610Sgdamore@opensolaris.org 	uint32_t	frame;
54611419Sgdamore@opensolaris.org 	uint32_t	tmp_mif;
54711419Sgdamore@opensolaris.org 	uint32_t	tmp_xif;
54811419Sgdamore@opensolaris.org 
54911419Sgdamore@opensolaris.org 	tmp_mif = GET_MIFREG(mif_cfg);
55011419Sgdamore@opensolaris.org 	tmp_xif = GET_MACREG(xifc);
55111419Sgdamore@opensolaris.org 
55211419Sgdamore@opensolaris.org 	switch (phyad) {
55311419Sgdamore@opensolaris.org 	case HME_EXTERNAL_PHYAD:
55411419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif | HME_MIF_CFGPS);
55511419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif | BMAC_XIFC_MIIBUFDIS);
55611419Sgdamore@opensolaris.org 		break;
55711419Sgdamore@opensolaris.org 	case HME_INTERNAL_PHYAD:
55811419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif & ~(HME_MIF_CFGPS));
55911419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif & ~(BMAC_XIFC_MIIBUFDIS));
56011419Sgdamore@opensolaris.org 		break;
56111419Sgdamore@opensolaris.org 	default:
56211419Sgdamore@opensolaris.org 		return (0xffff);
56311419Sgdamore@opensolaris.org 	}
56411419Sgdamore@opensolaris.org 
56511419Sgdamore@opensolaris.org 	if (!hmep->hme_frame_enable) {
56611419Sgdamore@opensolaris.org 		frame = (hme_bb_mii_read(hmep, phyad, regad));
56711419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif);
56811419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif);
56911419Sgdamore@opensolaris.org 		return (frame & 0xffff);
57011419Sgdamore@opensolaris.org 	}
5719610Sgdamore@opensolaris.org 
5729610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_frame,
5739610Sgdamore@opensolaris.org 	    HME_MIF_FRREAD | (phyad << HME_MIF_FRPHYAD_SHIFT) |
5749610Sgdamore@opensolaris.org 	    (regad << HME_MIF_FRREGAD_SHIFT));
5759610Sgdamore@opensolaris.org /*
5769610Sgdamore@opensolaris.org  *	HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
5779610Sgdamore@opensolaris.org  */
5789610Sgdamore@opensolaris.org 	HMEDELAY((GET_MIFREG(mif_frame) & HME_MIF_FRTA0), 300);
5799610Sgdamore@opensolaris.org 	frame = GET_MIFREG(mif_frame);
5809610Sgdamore@opensolaris.org 	CHECK_MIFREG();
58111419Sgdamore@opensolaris.org 
58211419Sgdamore@opensolaris.org 	PUT_MACREG(xifc, tmp_xif);
58311419Sgdamore@opensolaris.org 	PUT_MIFREG(mif_cfg, tmp_mif);
58411419Sgdamore@opensolaris.org 
5859610Sgdamore@opensolaris.org 	if ((frame & HME_MIF_FRTA0) == 0) {
5869610Sgdamore@opensolaris.org 
5879610Sgdamore@opensolaris.org 
58810806Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, MII_MSG,
5899610Sgdamore@opensolaris.org 		    "MIF Read failure");
59010806Sgdamore@opensolaris.org 		return (0xffff);
5919610Sgdamore@opensolaris.org 	}
59210806Sgdamore@opensolaris.org 	return ((uint16_t)(frame & HME_MIF_FRDATA));
5939610Sgdamore@opensolaris.org }
5949610Sgdamore@opensolaris.org 
5959610Sgdamore@opensolaris.org static void
hme_mii_write(void * arg,uint8_t phyad,uint8_t regad,uint16_t data)59610806Sgdamore@opensolaris.org hme_mii_write(void *arg, uint8_t phyad, uint8_t regad, uint16_t data)
5979610Sgdamore@opensolaris.org {
59810806Sgdamore@opensolaris.org 	struct hme *hmep = arg;
5999610Sgdamore@opensolaris.org 	uint32_t frame;
60011419Sgdamore@opensolaris.org 	uint32_t tmp_mif;
60111419Sgdamore@opensolaris.org 	uint32_t tmp_xif;
60211419Sgdamore@opensolaris.org 
60311419Sgdamore@opensolaris.org 	tmp_mif = GET_MIFREG(mif_cfg);
60411419Sgdamore@opensolaris.org 	tmp_xif = GET_MACREG(xifc);
60511419Sgdamore@opensolaris.org 
60611419Sgdamore@opensolaris.org 	switch (phyad) {
60711419Sgdamore@opensolaris.org 	case HME_EXTERNAL_PHYAD:
60811419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif | HME_MIF_CFGPS);
60911419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif | BMAC_XIFC_MIIBUFDIS);
61011419Sgdamore@opensolaris.org 		break;
61111419Sgdamore@opensolaris.org 	case HME_INTERNAL_PHYAD:
61211419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif & ~(HME_MIF_CFGPS));
61311419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif & ~(BMAC_XIFC_MIIBUFDIS));
61411419Sgdamore@opensolaris.org 		break;
61511419Sgdamore@opensolaris.org 	default:
61611419Sgdamore@opensolaris.org 		return;
61711419Sgdamore@opensolaris.org 	}
6189610Sgdamore@opensolaris.org 
6199610Sgdamore@opensolaris.org 	if (!hmep->hme_frame_enable) {
62010806Sgdamore@opensolaris.org 		hme_bb_mii_write(hmep, phyad, regad, data);
62111419Sgdamore@opensolaris.org 		PUT_MACREG(xifc, tmp_xif);
62211419Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, tmp_mif);
6239610Sgdamore@opensolaris.org 		return;
6249610Sgdamore@opensolaris.org 	}
6259610Sgdamore@opensolaris.org 
6269610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_frame,
6279610Sgdamore@opensolaris.org 	    HME_MIF_FRWRITE | (phyad << HME_MIF_FRPHYAD_SHIFT) |
6289610Sgdamore@opensolaris.org 	    (regad << HME_MIF_FRREGAD_SHIFT) | data);
6299610Sgdamore@opensolaris.org /*
6309610Sgdamore@opensolaris.org  *	HMEDELAY((*framerp & HME_MIF_FRTA0), HMEMAXRSTDELAY);
6319610Sgdamore@opensolaris.org  */
6329610Sgdamore@opensolaris.org 	HMEDELAY((GET_MIFREG(mif_frame) & HME_MIF_FRTA0), 300);
6339610Sgdamore@opensolaris.org 	frame = GET_MIFREG(mif_frame);
63411419Sgdamore@opensolaris.org 	PUT_MACREG(xifc, tmp_xif);
63511419Sgdamore@opensolaris.org 	PUT_MIFREG(mif_cfg, tmp_mif);
6369610Sgdamore@opensolaris.org 	CHECK_MIFREG();
6379610Sgdamore@opensolaris.org 	if ((frame & HME_MIF_FRTA0) == 0) {
63810806Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_MID, MII_MSG,
6399610Sgdamore@opensolaris.org 		    "MIF Write failure");
6409610Sgdamore@opensolaris.org 	}
6419610Sgdamore@opensolaris.org }
6429610Sgdamore@opensolaris.org 
6439610Sgdamore@opensolaris.org static void
hme_mii_notify(void * arg,link_state_t link)64410806Sgdamore@opensolaris.org hme_mii_notify(void *arg, link_state_t link)
6459610Sgdamore@opensolaris.org {
6469610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
64710806Sgdamore@opensolaris.org 
64810806Sgdamore@opensolaris.org 	if (link == LINK_STATE_UP) {
64910806Sgdamore@opensolaris.org 		(void) hmeinit(hmep);
6509610Sgdamore@opensolaris.org 	}
65110806Sgdamore@opensolaris.org 	mac_link_update(hmep->hme_mh, link);
6529610Sgdamore@opensolaris.org }
6539610Sgdamore@opensolaris.org 
6549610Sgdamore@opensolaris.org /* <<<<<<<<<<<<<<<<<<<<<<<<<<<  LOADABLE ENTRIES  >>>>>>>>>>>>>>>>>>>>>>> */
6559610Sgdamore@opensolaris.org 
6569610Sgdamore@opensolaris.org int
_init(void)6579610Sgdamore@opensolaris.org _init(void)
6589610Sgdamore@opensolaris.org {
6599610Sgdamore@opensolaris.org 	int	status;
6609610Sgdamore@opensolaris.org 
6619610Sgdamore@opensolaris.org 	mac_init_ops(&hme_dev_ops, "hme");
6629610Sgdamore@opensolaris.org 	if ((status = mod_install(&modlinkage)) != 0) {
6639610Sgdamore@opensolaris.org 		mac_fini_ops(&hme_dev_ops);
6649610Sgdamore@opensolaris.org 	}
6659610Sgdamore@opensolaris.org 	return (status);
6669610Sgdamore@opensolaris.org }
6679610Sgdamore@opensolaris.org 
6689610Sgdamore@opensolaris.org int
_fini(void)6699610Sgdamore@opensolaris.org _fini(void)
6709610Sgdamore@opensolaris.org {
6719610Sgdamore@opensolaris.org 	int	status;
6729610Sgdamore@opensolaris.org 
6739610Sgdamore@opensolaris.org 	if ((status = mod_remove(&modlinkage)) == 0) {
6749610Sgdamore@opensolaris.org 		mac_fini_ops(&hme_dev_ops);
6759610Sgdamore@opensolaris.org 	}
6769610Sgdamore@opensolaris.org 	return (status);
6779610Sgdamore@opensolaris.org }
6789610Sgdamore@opensolaris.org 
6799610Sgdamore@opensolaris.org int
_info(struct modinfo * modinfop)6809610Sgdamore@opensolaris.org _info(struct modinfo *modinfop)
6819610Sgdamore@opensolaris.org {
6829610Sgdamore@opensolaris.org 	return (mod_info(&modlinkage, modinfop));
6839610Sgdamore@opensolaris.org }
6849610Sgdamore@opensolaris.org 
6859610Sgdamore@opensolaris.org /*
6869610Sgdamore@opensolaris.org  * ddi_dma_sync() a TMD or RMD descriptor.
6879610Sgdamore@opensolaris.org  */
6889610Sgdamore@opensolaris.org #define	HMESYNCRMD(num, who)				\
6899610Sgdamore@opensolaris.org 	(void) ddi_dma_sync(hmep->hme_rmd_dmah,		\
6909610Sgdamore@opensolaris.org 	    (num * sizeof (struct hme_rmd)),		\
6919610Sgdamore@opensolaris.org 	    sizeof (struct hme_rmd),			\
6929610Sgdamore@opensolaris.org 	    who)
6939610Sgdamore@opensolaris.org 
6949610Sgdamore@opensolaris.org #define	HMESYNCTMD(num, who)				\
6959610Sgdamore@opensolaris.org 	(void) ddi_dma_sync(hmep->hme_tmd_dmah,		\
6969610Sgdamore@opensolaris.org 	    (num * sizeof (struct hme_tmd)),		\
6979610Sgdamore@opensolaris.org 	    sizeof (struct hme_tmd),			\
6989610Sgdamore@opensolaris.org 	    who)
6999610Sgdamore@opensolaris.org 
7009610Sgdamore@opensolaris.org /*
7019610Sgdamore@opensolaris.org  * Ethernet broadcast address definition.
7029610Sgdamore@opensolaris.org  */
7039610Sgdamore@opensolaris.org static	struct ether_addr	etherbroadcastaddr = {
7049610Sgdamore@opensolaris.org 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
7059610Sgdamore@opensolaris.org };
7069610Sgdamore@opensolaris.org 
7079610Sgdamore@opensolaris.org /*
7089610Sgdamore@opensolaris.org  * MIB II broadcast/multicast packets
7099610Sgdamore@opensolaris.org  */
7109610Sgdamore@opensolaris.org #define	IS_BROADCAST(pkt) (bcmp(pkt, &etherbroadcastaddr, ETHERADDRL) == 0)
7119610Sgdamore@opensolaris.org #define	IS_MULTICAST(pkt) ((pkt[0] & 01) == 1)
7129610Sgdamore@opensolaris.org #define	BUMP_InNUcast(hmep, pkt) \
7139610Sgdamore@opensolaris.org 	if (IS_MULTICAST(pkt)) {			       \
7149610Sgdamore@opensolaris.org 		if (IS_BROADCAST(pkt)) {		       \
7159610Sgdamore@opensolaris.org 			hmep->hme_brdcstrcv++;		       \
7169610Sgdamore@opensolaris.org 		} else {				       \
7179610Sgdamore@opensolaris.org 			hmep->hme_multircv++;		       \
7189610Sgdamore@opensolaris.org 		}					       \
7199610Sgdamore@opensolaris.org 	}
7209610Sgdamore@opensolaris.org #define	BUMP_OutNUcast(hmep, pkt) \
7219610Sgdamore@opensolaris.org 	if (IS_MULTICAST(pkt)) {			       \
7229610Sgdamore@opensolaris.org 		if (IS_BROADCAST(pkt)) {		       \
7239610Sgdamore@opensolaris.org 			hmep->hme_brdcstxmt++;		       \
7249610Sgdamore@opensolaris.org 		} else {				       \
7259610Sgdamore@opensolaris.org 			hmep->hme_multixmt++;		       \
7269610Sgdamore@opensolaris.org 		}					       \
7279610Sgdamore@opensolaris.org 	}
7289610Sgdamore@opensolaris.org 
7299610Sgdamore@opensolaris.org static int
hme_create_prop_from_kw(dev_info_t * dip,char * vpdname,char * vpdstr)7309610Sgdamore@opensolaris.org hme_create_prop_from_kw(dev_info_t *dip, char *vpdname, char *vpdstr)
7319610Sgdamore@opensolaris.org {
7329610Sgdamore@opensolaris.org 	char propstr[80];
7339610Sgdamore@opensolaris.org 	int i, needprop = 0;
7349610Sgdamore@opensolaris.org 	struct ether_addr local_mac;
7359610Sgdamore@opensolaris.org 
7369610Sgdamore@opensolaris.org 	if (strcmp(vpdname, "NA") == 0) {
7379610Sgdamore@opensolaris.org 		(void) strcpy(propstr, "local-mac-address");
7389610Sgdamore@opensolaris.org 		needprop = 1;
7399610Sgdamore@opensolaris.org 	} else if (strcmp(vpdname, "Z0") == 0) {
7409610Sgdamore@opensolaris.org 		(void) strcpy(propstr, "model");
7419610Sgdamore@opensolaris.org 		needprop = 1;
7429610Sgdamore@opensolaris.org 	} else if (strcmp(vpdname, "Z1") == 0) {
7439610Sgdamore@opensolaris.org 		(void) strcpy(propstr, "board-model");
7449610Sgdamore@opensolaris.org 		needprop = 1;
7459610Sgdamore@opensolaris.org 	}
7469610Sgdamore@opensolaris.org 
7479610Sgdamore@opensolaris.org 	if (needprop == 1) {
7489610Sgdamore@opensolaris.org 
7499610Sgdamore@opensolaris.org 		if (strcmp(propstr, "local-mac-address") == 0) {
7509610Sgdamore@opensolaris.org 			for (i = 0; i < ETHERADDRL; i++)
7519610Sgdamore@opensolaris.org 				local_mac.ether_addr_octet[i] =
7529610Sgdamore@opensolaris.org 				    (uchar_t)vpdstr[i];
7539610Sgdamore@opensolaris.org 			if (ddi_prop_create(DDI_DEV_T_NONE, dip,
7549610Sgdamore@opensolaris.org 			    DDI_PROP_CANSLEEP, propstr,
7559610Sgdamore@opensolaris.org 			    (char *)local_mac.ether_addr_octet, ETHERADDRL)
7569610Sgdamore@opensolaris.org 			    != DDI_SUCCESS) {
7579610Sgdamore@opensolaris.org 				return (DDI_FAILURE);
7589610Sgdamore@opensolaris.org 			}
7599610Sgdamore@opensolaris.org 		} else {
7609610Sgdamore@opensolaris.org 			if (ddi_prop_create(DDI_DEV_T_NONE, dip,
7619610Sgdamore@opensolaris.org 			    DDI_PROP_CANSLEEP, propstr, vpdstr,
7629610Sgdamore@opensolaris.org 			    strlen(vpdstr)+1) != DDI_SUCCESS) {
7639610Sgdamore@opensolaris.org 				return (DDI_FAILURE);
7649610Sgdamore@opensolaris.org 			}
7659610Sgdamore@opensolaris.org 		}
7669610Sgdamore@opensolaris.org 	}
7679610Sgdamore@opensolaris.org 	return (0);
7689610Sgdamore@opensolaris.org }
7699610Sgdamore@opensolaris.org 
7709610Sgdamore@opensolaris.org /*
7719610Sgdamore@opensolaris.org  * Get properties from old VPD
7729610Sgdamore@opensolaris.org  * for PCI cards
7739610Sgdamore@opensolaris.org  */
7749610Sgdamore@opensolaris.org static int
hme_get_oldvpd_props(dev_info_t * dip,int vpd_base)7759610Sgdamore@opensolaris.org hme_get_oldvpd_props(dev_info_t *dip, int vpd_base)
7769610Sgdamore@opensolaris.org {
7779610Sgdamore@opensolaris.org 	struct hme *hmep;
7789610Sgdamore@opensolaris.org 	int vpd_start, vpd_len, kw_start, kw_len, kw_ptr;
7799610Sgdamore@opensolaris.org 	char kw_namestr[3];
7809610Sgdamore@opensolaris.org 	char kw_fieldstr[256];
7819610Sgdamore@opensolaris.org 	int i;
7829610Sgdamore@opensolaris.org 
7839610Sgdamore@opensolaris.org 	hmep = ddi_get_driver_private(dip);
7849610Sgdamore@opensolaris.org 
7859610Sgdamore@opensolaris.org 	vpd_start = vpd_base;
7869610Sgdamore@opensolaris.org 
7879610Sgdamore@opensolaris.org 	if ((GET_ROM8(&hmep->hme_romp[vpd_start]) & 0xff) != 0x90) {
7889610Sgdamore@opensolaris.org 		return (1); /* error */
7899610Sgdamore@opensolaris.org 	} else {
7909610Sgdamore@opensolaris.org 		vpd_len = 9;
7919610Sgdamore@opensolaris.org 	}
7929610Sgdamore@opensolaris.org 
7939610Sgdamore@opensolaris.org 	/* Get local-mac-address */
7949610Sgdamore@opensolaris.org 	kw_start = vpd_start + 3; /* Location of 1st keyword */
7959610Sgdamore@opensolaris.org 	kw_ptr = kw_start;
7969610Sgdamore@opensolaris.org 	while ((kw_ptr - kw_start) < vpd_len) { /* Get all keywords */
7979610Sgdamore@opensolaris.org 		kw_namestr[0] = GET_ROM8(&hmep->hme_romp[kw_ptr]);
7989610Sgdamore@opensolaris.org 		kw_namestr[1] = GET_ROM8(&hmep->hme_romp[kw_ptr+1]);
7999610Sgdamore@opensolaris.org 		kw_namestr[2] = '\0';
8009610Sgdamore@opensolaris.org 		kw_len = (int)(GET_ROM8(&hmep->hme_romp[kw_ptr+2]) & 0xff);
8019610Sgdamore@opensolaris.org 		for (i = 0, kw_ptr += 3; i < kw_len; i++)
8029610Sgdamore@opensolaris.org 			kw_fieldstr[i] = GET_ROM8(&hmep->hme_romp[kw_ptr+i]);
8039610Sgdamore@opensolaris.org 		kw_fieldstr[i] = '\0';
8049610Sgdamore@opensolaris.org 		if (hme_create_prop_from_kw(dip, kw_namestr, kw_fieldstr)) {
8059610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
8069610Sgdamore@opensolaris.org 		}
8079610Sgdamore@opensolaris.org 		kw_ptr += kw_len;
8089610Sgdamore@opensolaris.org 	} /* next keyword */
8099610Sgdamore@opensolaris.org 
8109610Sgdamore@opensolaris.org 	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, "model",
8119610Sgdamore@opensolaris.org 	    "SUNW,cheerio", strlen("SUNW,cheerio")+1) != DDI_SUCCESS) {
8129610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
8139610Sgdamore@opensolaris.org 	}
8149610Sgdamore@opensolaris.org 	return (0);
8159610Sgdamore@opensolaris.org }
8169610Sgdamore@opensolaris.org 
8179610Sgdamore@opensolaris.org 
8189610Sgdamore@opensolaris.org /*
8199610Sgdamore@opensolaris.org  * Get properties from new VPD
8209610Sgdamore@opensolaris.org  * for CompactPCI cards
8219610Sgdamore@opensolaris.org  */
8229610Sgdamore@opensolaris.org static int
hme_get_newvpd_props(dev_info_t * dip,int vpd_base)8239610Sgdamore@opensolaris.org hme_get_newvpd_props(dev_info_t *dip, int vpd_base)
8249610Sgdamore@opensolaris.org {
8259610Sgdamore@opensolaris.org 	struct hme *hmep;
8269610Sgdamore@opensolaris.org 	int vpd_start, vpd_len, kw_start, kw_len, kw_ptr;
8279610Sgdamore@opensolaris.org 	char kw_namestr[3];
8289610Sgdamore@opensolaris.org 	char kw_fieldstr[256];
8299610Sgdamore@opensolaris.org 	int maxvpdsize, i;
8309610Sgdamore@opensolaris.org 
8319610Sgdamore@opensolaris.org 	hmep = ddi_get_driver_private(dip);
8329610Sgdamore@opensolaris.org 
8339610Sgdamore@opensolaris.org 	maxvpdsize = 1024; /* Real size not known until after it is read */
8349610Sgdamore@opensolaris.org 
8359610Sgdamore@opensolaris.org 	vpd_start = (int)((GET_ROM8(&(hmep->hme_romp[vpd_base+1])) & 0xff) |
8369610Sgdamore@opensolaris.org 	    ((GET_ROM8(&hmep->hme_romp[vpd_base+2]) & 0xff) << 8)) +3;
8379610Sgdamore@opensolaris.org 	vpd_start = vpd_base + vpd_start;
8389610Sgdamore@opensolaris.org 	while (vpd_start < (vpd_base + maxvpdsize)) { /* Get all VPDs */
8399610Sgdamore@opensolaris.org 		if ((GET_ROM8(&hmep->hme_romp[vpd_start]) & 0xff) != 0x90) {
8409610Sgdamore@opensolaris.org 			break; /* no VPD found */
8419610Sgdamore@opensolaris.org 		} else {
8429610Sgdamore@opensolaris.org 			vpd_len = (int)((GET_ROM8(&hmep->hme_romp[vpd_start
8439610Sgdamore@opensolaris.org 			    + 1]) & 0xff) | (GET_ROM8(&hmep->hme_romp[vpd_start
8449610Sgdamore@opensolaris.org 			    + 2]) & 0xff) << 8);
8459610Sgdamore@opensolaris.org 		}
8469610Sgdamore@opensolaris.org 		/* Get all keywords in this VPD */
8479610Sgdamore@opensolaris.org 		kw_start = vpd_start + 3; /* Location of 1st keyword */
8489610Sgdamore@opensolaris.org 		kw_ptr = kw_start;
8499610Sgdamore@opensolaris.org 		while ((kw_ptr - kw_start) < vpd_len) { /* Get all keywords */
8509610Sgdamore@opensolaris.org 			kw_namestr[0] = GET_ROM8(&hmep->hme_romp[kw_ptr]);
8519610Sgdamore@opensolaris.org 			kw_namestr[1] = GET_ROM8(&hmep->hme_romp[kw_ptr+1]);
8529610Sgdamore@opensolaris.org 			kw_namestr[2] = '\0';
8539610Sgdamore@opensolaris.org 			kw_len =
8549610Sgdamore@opensolaris.org 			    (int)(GET_ROM8(&hmep->hme_romp[kw_ptr+2]) & 0xff);
8559610Sgdamore@opensolaris.org 			for (i = 0, kw_ptr += 3; i < kw_len; i++)
8569610Sgdamore@opensolaris.org 				kw_fieldstr[i] =
8579610Sgdamore@opensolaris.org 				    GET_ROM8(&hmep->hme_romp[kw_ptr+i]);
8589610Sgdamore@opensolaris.org 			kw_fieldstr[i] = '\0';
8599610Sgdamore@opensolaris.org 			if (hme_create_prop_from_kw(dip, kw_namestr,
8609610Sgdamore@opensolaris.org 			    kw_fieldstr)) {
8619610Sgdamore@opensolaris.org 				return (DDI_FAILURE);
8629610Sgdamore@opensolaris.org 			}
8639610Sgdamore@opensolaris.org 			kw_ptr += kw_len;
8649610Sgdamore@opensolaris.org 		} /* next keyword */
8659610Sgdamore@opensolaris.org 		vpd_start += (vpd_len + 3);
8669610Sgdamore@opensolaris.org 	} /* next VPD */
8679610Sgdamore@opensolaris.org 	return (0);
8689610Sgdamore@opensolaris.org }
8699610Sgdamore@opensolaris.org 
8709610Sgdamore@opensolaris.org 
8719610Sgdamore@opensolaris.org /*
8729610Sgdamore@opensolaris.org  * Get properties from VPD
8739610Sgdamore@opensolaris.org  */
8749610Sgdamore@opensolaris.org static int
hme_get_vpd_props(dev_info_t * dip)8759610Sgdamore@opensolaris.org hme_get_vpd_props(dev_info_t *dip)
8769610Sgdamore@opensolaris.org {
8779610Sgdamore@opensolaris.org 	struct hme *hmep;
8789610Sgdamore@opensolaris.org 	int v0, v1, vpd_base;
8799610Sgdamore@opensolaris.org 	int i, epromsrchlimit;
8809610Sgdamore@opensolaris.org 
8819610Sgdamore@opensolaris.org 
8829610Sgdamore@opensolaris.org 	hmep = ddi_get_driver_private(dip);
8839610Sgdamore@opensolaris.org 
8849610Sgdamore@opensolaris.org 	v0 = (int)(GET_ROM8(&(hmep->hme_romp[0])));
8859610Sgdamore@opensolaris.org 	v1 = (int)(GET_ROM8(&(hmep->hme_romp[1])));
8869610Sgdamore@opensolaris.org 	v0 = ((v0 & 0xff) << 8 | v1);
8879610Sgdamore@opensolaris.org 
8889610Sgdamore@opensolaris.org 	if ((v0 & 0xffff) != 0x55aa) {
8899610Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, " Valid pci prom not found \n");
8909610Sgdamore@opensolaris.org 		return (1);
8919610Sgdamore@opensolaris.org 	}
8929610Sgdamore@opensolaris.org 
8939610Sgdamore@opensolaris.org 	epromsrchlimit = 4096;
8949610Sgdamore@opensolaris.org 	for (i = 2; i < epromsrchlimit; i++) {
8959610Sgdamore@opensolaris.org 		/* "PCIR" */
8969610Sgdamore@opensolaris.org 		if (((GET_ROM8(&(hmep->hme_romp[i])) & 0xff) == 'P') &&
8979610Sgdamore@opensolaris.org 		    ((GET_ROM8(&(hmep->hme_romp[i+1])) & 0xff) == 'C') &&
8989610Sgdamore@opensolaris.org 		    ((GET_ROM8(&(hmep->hme_romp[i+2])) & 0xff) == 'I') &&
8999610Sgdamore@opensolaris.org 		    ((GET_ROM8(&(hmep->hme_romp[i+3])) & 0xff) == 'R')) {
9009610Sgdamore@opensolaris.org 			vpd_base =
9019610Sgdamore@opensolaris.org 			    (int)((GET_ROM8(&(hmep->hme_romp[i+8])) & 0xff) |
9029610Sgdamore@opensolaris.org 			    (GET_ROM8(&(hmep->hme_romp[i+9])) & 0xff) << 8);
9039610Sgdamore@opensolaris.org 			break; /* VPD pointer found */
9049610Sgdamore@opensolaris.org 		}
9059610Sgdamore@opensolaris.org 	}
9069610Sgdamore@opensolaris.org 
9079610Sgdamore@opensolaris.org 	/* No VPD found */
9089610Sgdamore@opensolaris.org 	if (vpd_base == 0) {
9099610Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, " Vital Product Data pointer not found \n");
9109610Sgdamore@opensolaris.org 		return (1);
9119610Sgdamore@opensolaris.org 	}
9129610Sgdamore@opensolaris.org 
9139610Sgdamore@opensolaris.org 	v0 = (int)(GET_ROM8(&(hmep->hme_romp[vpd_base])));
9149610Sgdamore@opensolaris.org 	if (v0 == 0x82) {
9159610Sgdamore@opensolaris.org 		if (hme_get_newvpd_props(dip, vpd_base))
9169610Sgdamore@opensolaris.org 			return (1);
9179610Sgdamore@opensolaris.org 		return (0);
9189610Sgdamore@opensolaris.org 	} else if (v0 == 0x90) {
9199610Sgdamore@opensolaris.org 		/* If we are are SUNW,qfe card, look for the Nth "NA" descr */
9209610Sgdamore@opensolaris.org 		if ((GET_ROM8(&hmep->hme_romp[vpd_base + 12])  != 0x79) &&
9219610Sgdamore@opensolaris.org 		    GET_ROM8(&hmep->hme_romp[vpd_base + 4 * 12]) == 0x79) {
9229610Sgdamore@opensolaris.org 			vpd_base += hmep->hme_devno * 12;
9239610Sgdamore@opensolaris.org 		}
9249610Sgdamore@opensolaris.org 		if (hme_get_oldvpd_props(dip, vpd_base))
9259610Sgdamore@opensolaris.org 			return (1);
9269610Sgdamore@opensolaris.org 		return (0);
9279610Sgdamore@opensolaris.org 	} else
9289610Sgdamore@opensolaris.org 		return (1);	/* unknown start byte in VPD */
9299610Sgdamore@opensolaris.org }
9309610Sgdamore@opensolaris.org 
9319610Sgdamore@opensolaris.org /*
9329610Sgdamore@opensolaris.org  * For x86, the BIOS doesn't map the PCI Rom register for the qfe
9339610Sgdamore@opensolaris.org  * cards, so we have to extract it from the ebus bridge that is
9349610Sgdamore@opensolaris.org  * function zero of the same device.  This is a bit of an ugly hack.
9359610Sgdamore@opensolaris.org  * (The ebus bridge leaves the entire ROM mapped at base address
9369610Sgdamore@opensolaris.org  * register 0x10.)
9379610Sgdamore@opensolaris.org  */
9389610Sgdamore@opensolaris.org 
9399610Sgdamore@opensolaris.org typedef struct {
9409610Sgdamore@opensolaris.org 	struct hme 		*hmep;
9419610Sgdamore@opensolaris.org 	dev_info_t		*parent;
9429610Sgdamore@opensolaris.org 	uint8_t			bus, dev;
9439610Sgdamore@opensolaris.org 	ddi_acc_handle_t	acch;
9449610Sgdamore@opensolaris.org 	caddr_t			romp;
9459610Sgdamore@opensolaris.org } ebus_rom_t;
9469610Sgdamore@opensolaris.org 
9479610Sgdamore@opensolaris.org static int
hme_mapebusrom(dev_info_t * dip,void * arg)9489610Sgdamore@opensolaris.org hme_mapebusrom(dev_info_t *dip, void *arg)
9499610Sgdamore@opensolaris.org {
9509610Sgdamore@opensolaris.org 	int		*regs;
9519610Sgdamore@opensolaris.org 	unsigned	nregs;
9529610Sgdamore@opensolaris.org 	int		reg;
9539610Sgdamore@opensolaris.org 	ebus_rom_t	*rom = arg;
9549610Sgdamore@opensolaris.org 	struct hme	*hmep = rom->hmep;
9559610Sgdamore@opensolaris.org 
9569610Sgdamore@opensolaris.org 	/*
9579610Sgdamore@opensolaris.org 	 * We only want to look at our peers.  Skip our parent.
9589610Sgdamore@opensolaris.org 	 */
9599610Sgdamore@opensolaris.org 	if (dip == rom->parent) {
9609610Sgdamore@opensolaris.org 		return (DDI_WALK_PRUNESIB);
9619610Sgdamore@opensolaris.org 	}
9629610Sgdamore@opensolaris.org 
96310806Sgdamore@opensolaris.org 	if (ddi_get_parent(dip) != rom->parent)
96410806Sgdamore@opensolaris.org 		return (DDI_WALK_CONTINUE);
96510806Sgdamore@opensolaris.org 
9669610Sgdamore@opensolaris.org 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0,
9679610Sgdamore@opensolaris.org 	    "reg", &regs, &nregs)) != DDI_PROP_SUCCESS) {
9689610Sgdamore@opensolaris.org 		return (DDI_WALK_PRUNECHILD);
9699610Sgdamore@opensolaris.org 	}
9709610Sgdamore@opensolaris.org 
9719610Sgdamore@opensolaris.org 	if (nregs < 1) {
9729610Sgdamore@opensolaris.org 		ddi_prop_free(regs);
9739610Sgdamore@opensolaris.org 		return (DDI_WALK_PRUNECHILD);
9749610Sgdamore@opensolaris.org 	}
9759610Sgdamore@opensolaris.org 	reg = regs[0];
9769610Sgdamore@opensolaris.org 	ddi_prop_free(regs);
9779610Sgdamore@opensolaris.org 
9789610Sgdamore@opensolaris.org 	/*
9799610Sgdamore@opensolaris.org 	 * Look for function 0 on our bus and device.  If the device doesn't
9809610Sgdamore@opensolaris.org 	 * match, it might be an alternate peer, in which case we don't want
9819610Sgdamore@opensolaris.org 	 * to examine any of its children.
9829610Sgdamore@opensolaris.org 	 */
9839610Sgdamore@opensolaris.org 	if ((PCI_REG_BUS_G(reg) != rom->bus) ||
9849610Sgdamore@opensolaris.org 	    (PCI_REG_DEV_G(reg) != rom->dev) ||
9859610Sgdamore@opensolaris.org 	    (PCI_REG_FUNC_G(reg) != 0)) {
9869610Sgdamore@opensolaris.org 		return (DDI_WALK_PRUNECHILD);
9879610Sgdamore@opensolaris.org 	}
9889610Sgdamore@opensolaris.org 
9899610Sgdamore@opensolaris.org 	(void) ddi_regs_map_setup(dip, 1, &rom->romp, 0, 0, &hmep->hme_dev_attr,
9909610Sgdamore@opensolaris.org 	    &rom->acch);
9919610Sgdamore@opensolaris.org 	/*
9929610Sgdamore@opensolaris.org 	 * If we can't map the registers, the caller will notice that
9939610Sgdamore@opensolaris.org 	 * the acch is NULL.
9949610Sgdamore@opensolaris.org 	 */
9959610Sgdamore@opensolaris.org 	return (DDI_WALK_TERMINATE);
9969610Sgdamore@opensolaris.org }
9979610Sgdamore@opensolaris.org 
9989610Sgdamore@opensolaris.org static int
hmeget_promebus(dev_info_t * dip)9999610Sgdamore@opensolaris.org hmeget_promebus(dev_info_t *dip)
10009610Sgdamore@opensolaris.org {
10019610Sgdamore@opensolaris.org 	ebus_rom_t	rom;
10029610Sgdamore@opensolaris.org 	int		*regs;
10039610Sgdamore@opensolaris.org 	unsigned	nregs;
10049610Sgdamore@opensolaris.org 	struct hme	*hmep;
10059610Sgdamore@opensolaris.org 
10069610Sgdamore@opensolaris.org 	hmep = ddi_get_driver_private(dip);
10079610Sgdamore@opensolaris.org 
10089610Sgdamore@opensolaris.org 	bzero(&rom, sizeof (rom));
10099610Sgdamore@opensolaris.org 
10109610Sgdamore@opensolaris.org 	/*
10119610Sgdamore@opensolaris.org 	 * For x86, the BIOS doesn't map the PCI Rom register for the qfe
10129610Sgdamore@opensolaris.org 	 * cards, so we have to extract it from the eBus bridge that is
10139610Sgdamore@opensolaris.org 	 * function zero.  This is a bit of an ugly hack.
10149610Sgdamore@opensolaris.org 	 */
10159610Sgdamore@opensolaris.org 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 0,
10169610Sgdamore@opensolaris.org 	    "reg", &regs, &nregs)) != DDI_PROP_SUCCESS) {
10179610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
10189610Sgdamore@opensolaris.org 	}
10199610Sgdamore@opensolaris.org 
10209610Sgdamore@opensolaris.org 	if (nregs < 5) {
10219610Sgdamore@opensolaris.org 		ddi_prop_free(regs);
10229610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
10239610Sgdamore@opensolaris.org 	}
10249610Sgdamore@opensolaris.org 	rom.hmep = hmep;
10259610Sgdamore@opensolaris.org 	rom.bus = PCI_REG_BUS_G(regs[0]);
10269610Sgdamore@opensolaris.org 	rom.dev = PCI_REG_DEV_G(regs[0]);
10279610Sgdamore@opensolaris.org 	hmep->hme_devno = rom.dev;
10289610Sgdamore@opensolaris.org 	rom.parent = ddi_get_parent(dip);
10299610Sgdamore@opensolaris.org 
10309610Sgdamore@opensolaris.org 	/*
10319610Sgdamore@opensolaris.org 	 * The implementation of ddi_walk_devs says that we must not
103210806Sgdamore@opensolaris.org 	 * be called during autoconfiguration.  However, it turns out
103310806Sgdamore@opensolaris.org 	 * that it is safe to call this during our attach routine,
103410806Sgdamore@opensolaris.org 	 * because we are not a nexus device.
10359610Sgdamore@opensolaris.org 	 *
103610806Sgdamore@opensolaris.org 	 * Previously we rooted our search at our immediate parent,
103710806Sgdamore@opensolaris.org 	 * but this triggered an assertion panic in debug kernels.
10389610Sgdamore@opensolaris.org 	 */
103910806Sgdamore@opensolaris.org 	ddi_walk_devs(ddi_root_node(), hme_mapebusrom, &rom);
10409610Sgdamore@opensolaris.org 
10419610Sgdamore@opensolaris.org 	if (rom.acch) {
10429610Sgdamore@opensolaris.org 		hmep->hme_romh = rom.acch;
10439610Sgdamore@opensolaris.org 		hmep->hme_romp = (unsigned char *)rom.romp;
10449610Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
10459610Sgdamore@opensolaris.org 	}
10469610Sgdamore@opensolaris.org 	return (DDI_FAILURE);
10479610Sgdamore@opensolaris.org }
10489610Sgdamore@opensolaris.org 
10499610Sgdamore@opensolaris.org static int
hmeget_promprops(dev_info_t * dip)10509610Sgdamore@opensolaris.org hmeget_promprops(dev_info_t *dip)
10519610Sgdamore@opensolaris.org {
10529610Sgdamore@opensolaris.org 	struct hme *hmep;
10539610Sgdamore@opensolaris.org 	int rom_bar;
10549610Sgdamore@opensolaris.org 	ddi_acc_handle_t cfg_handle;
10559610Sgdamore@opensolaris.org 	struct {
10569610Sgdamore@opensolaris.org 		uint16_t vendorid;
10579610Sgdamore@opensolaris.org 		uint16_t devid;
10589610Sgdamore@opensolaris.org 		uint16_t command;
10599610Sgdamore@opensolaris.org 		uint16_t status;
10609610Sgdamore@opensolaris.org 		uint32_t junk1;
10619610Sgdamore@opensolaris.org 		uint8_t cache_line;
10629610Sgdamore@opensolaris.org 		uint8_t latency;
10639610Sgdamore@opensolaris.org 		uint8_t header;
10649610Sgdamore@opensolaris.org 		uint8_t bist;
10659610Sgdamore@opensolaris.org 		uint32_t base;
10669610Sgdamore@opensolaris.org 		uint32_t base14;
10679610Sgdamore@opensolaris.org 		uint32_t base18;
10689610Sgdamore@opensolaris.org 		uint32_t base1c;
10699610Sgdamore@opensolaris.org 		uint32_t base20;
10709610Sgdamore@opensolaris.org 		uint32_t base24;
10719610Sgdamore@opensolaris.org 		uint32_t base28;
10729610Sgdamore@opensolaris.org 		uint32_t base2c;
10739610Sgdamore@opensolaris.org 		uint32_t base30;
10749610Sgdamore@opensolaris.org 	} *cfg_ptr;
10759610Sgdamore@opensolaris.org 
10769610Sgdamore@opensolaris.org 	hmep = ddi_get_driver_private(dip);
10779610Sgdamore@opensolaris.org 
10789610Sgdamore@opensolaris.org 
10799610Sgdamore@opensolaris.org 	/*
10809610Sgdamore@opensolaris.org 	 * map configuration space
10819610Sgdamore@opensolaris.org 	 */
10829610Sgdamore@opensolaris.org 	if (ddi_regs_map_setup(hmep->dip, 0, (caddr_t *)&cfg_ptr,
10839610Sgdamore@opensolaris.org 	    0, 0, &hmep->hme_dev_attr, &cfg_handle)) {
10849610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
10859610Sgdamore@opensolaris.org 	}
10869610Sgdamore@opensolaris.org 
10879610Sgdamore@opensolaris.org 	/*
10889610Sgdamore@opensolaris.org 	 * Enable bus-master and memory accesses
10899610Sgdamore@opensolaris.org 	 */
10909610Sgdamore@opensolaris.org 	ddi_put16(cfg_handle, &cfg_ptr->command,
10919610Sgdamore@opensolaris.org 	    PCI_COMM_SERR_ENABLE | PCI_COMM_PARITY_DETECT |
10929610Sgdamore@opensolaris.org 	    PCI_COMM_MAE | PCI_COMM_ME);
10939610Sgdamore@opensolaris.org 
10949610Sgdamore@opensolaris.org 	/*
10959610Sgdamore@opensolaris.org 	 * Enable rom accesses
10969610Sgdamore@opensolaris.org 	 */
10979610Sgdamore@opensolaris.org 	rom_bar = ddi_get32(cfg_handle, &cfg_ptr->base30);
10989610Sgdamore@opensolaris.org 	ddi_put32(cfg_handle, &cfg_ptr->base30, rom_bar | 1);
10999610Sgdamore@opensolaris.org 
11009610Sgdamore@opensolaris.org 
11019610Sgdamore@opensolaris.org 	if ((ddi_regs_map_setup(dip, 2, (caddr_t *)&(hmep->hme_romp), 0, 0,
11029610Sgdamore@opensolaris.org 	    &hmep->hme_dev_attr, &hmep->hme_romh) != DDI_SUCCESS) &&
11039610Sgdamore@opensolaris.org 	    (hmeget_promebus(dip) != DDI_SUCCESS)) {
11049610Sgdamore@opensolaris.org 
11059610Sgdamore@opensolaris.org 		if (cfg_ptr)
11069610Sgdamore@opensolaris.org 			ddi_regs_map_free(&cfg_handle);
11079610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
11089610Sgdamore@opensolaris.org 	} else {
11099610Sgdamore@opensolaris.org 		if (hme_get_vpd_props(dip))
11109610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
11119610Sgdamore@opensolaris.org 	}
11129610Sgdamore@opensolaris.org 	if (hmep->hme_romp)
11139610Sgdamore@opensolaris.org 		ddi_regs_map_free(&hmep->hme_romh);
11149610Sgdamore@opensolaris.org 	if (cfg_ptr)
11159610Sgdamore@opensolaris.org 		ddi_regs_map_free(&cfg_handle);
11169610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
11179610Sgdamore@opensolaris.org 
11189610Sgdamore@opensolaris.org }
11199610Sgdamore@opensolaris.org 
11209610Sgdamore@opensolaris.org static void
hmeget_hm_rev_property(struct hme * hmep)11219610Sgdamore@opensolaris.org hmeget_hm_rev_property(struct hme *hmep)
11229610Sgdamore@opensolaris.org {
11239610Sgdamore@opensolaris.org 	int	hm_rev;
11249610Sgdamore@opensolaris.org 
11259610Sgdamore@opensolaris.org 
11269610Sgdamore@opensolaris.org 	hm_rev = hmep->asic_rev;
11279610Sgdamore@opensolaris.org 	switch (hm_rev) {
11289610Sgdamore@opensolaris.org 	case HME_2P1_REVID:
11299610Sgdamore@opensolaris.org 	case HME_2P1_REVID_OBP:
11309610Sgdamore@opensolaris.org 		HME_FAULT_MSG2(hmep, SEVERITY_NONE, DISPLAY_MSG,
11319610Sgdamore@opensolaris.org 		    "SBus 2.1 Found (Rev Id = %x)", hm_rev);
11329610Sgdamore@opensolaris.org 		hmep->hme_frame_enable = 1;
11339610Sgdamore@opensolaris.org 		break;
11349610Sgdamore@opensolaris.org 
11359610Sgdamore@opensolaris.org 	case HME_2P0_REVID:
11369610Sgdamore@opensolaris.org 		HME_FAULT_MSG2(hmep, SEVERITY_NONE, DISPLAY_MSG,
11379610Sgdamore@opensolaris.org 		    "SBus 2.0 Found (Rev Id = %x)", hm_rev);
11389610Sgdamore@opensolaris.org 		break;
11399610Sgdamore@opensolaris.org 
11409610Sgdamore@opensolaris.org 	case HME_1C0_REVID:
11419610Sgdamore@opensolaris.org 		HME_FAULT_MSG2(hmep, SEVERITY_NONE, DISPLAY_MSG,
11429610Sgdamore@opensolaris.org 		    "PCI IO 1.0 Found (Rev Id = %x)", hm_rev);
11439610Sgdamore@opensolaris.org 		break;
11449610Sgdamore@opensolaris.org 
11459610Sgdamore@opensolaris.org 	default:
114610806Sgdamore@opensolaris.org 		HME_FAULT_MSG3(hmep, SEVERITY_NONE, DISPLAY_MSG,
11479610Sgdamore@opensolaris.org 		    "%s (Rev Id = %x) Found",
11489610Sgdamore@opensolaris.org 		    (hm_rev == HME_2C0_REVID) ? "PCI IO 2.0" : "Sbus", hm_rev);
11499610Sgdamore@opensolaris.org 		hmep->hme_frame_enable = 1;
11509610Sgdamore@opensolaris.org 		hmep->hme_lance_mode_enable = 1;
11519610Sgdamore@opensolaris.org 		hmep->hme_rxcv_enable = 1;
11529610Sgdamore@opensolaris.org 		break;
11539610Sgdamore@opensolaris.org 	}
11549610Sgdamore@opensolaris.org }
11559610Sgdamore@opensolaris.org 
11569610Sgdamore@opensolaris.org /*
11579610Sgdamore@opensolaris.org  * Interface exists: make available by filling in network interface
11589610Sgdamore@opensolaris.org  * record.  System will initialize the interface when it is ready
11599610Sgdamore@opensolaris.org  * to accept packets.
11609610Sgdamore@opensolaris.org  */
11619610Sgdamore@opensolaris.org int
hmeattach(dev_info_t * dip,ddi_attach_cmd_t cmd)11629610Sgdamore@opensolaris.org hmeattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
11639610Sgdamore@opensolaris.org {
11649610Sgdamore@opensolaris.org 	struct hme *hmep;
11659610Sgdamore@opensolaris.org 	mac_register_t *macp = NULL;
11669610Sgdamore@opensolaris.org 	int 	regno;
11679610Sgdamore@opensolaris.org 	int hm_rev = 0;
11689610Sgdamore@opensolaris.org 	int prop_len = sizeof (int);
11699610Sgdamore@opensolaris.org 	ddi_acc_handle_t cfg_handle;
11709610Sgdamore@opensolaris.org 	struct {
11719610Sgdamore@opensolaris.org 		uint16_t vendorid;
11729610Sgdamore@opensolaris.org 		uint16_t devid;
11739610Sgdamore@opensolaris.org 		uint16_t command;
11749610Sgdamore@opensolaris.org 		uint16_t status;
11759610Sgdamore@opensolaris.org 		uint8_t revid;
11769610Sgdamore@opensolaris.org 		uint8_t j1;
11779610Sgdamore@opensolaris.org 		uint16_t j2;
11789610Sgdamore@opensolaris.org 	} *cfg_ptr;
11799610Sgdamore@opensolaris.org 
11809610Sgdamore@opensolaris.org 	switch (cmd) {
11819610Sgdamore@opensolaris.org 	case DDI_ATTACH:
11829610Sgdamore@opensolaris.org 		break;
11839610Sgdamore@opensolaris.org 
11849610Sgdamore@opensolaris.org 	case DDI_RESUME:
11859610Sgdamore@opensolaris.org 		if ((hmep = ddi_get_driver_private(dip)) == NULL)
11869610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
11879610Sgdamore@opensolaris.org 
11889610Sgdamore@opensolaris.org 		hmep->hme_flags &= ~HMESUSPENDED;
118910806Sgdamore@opensolaris.org 
119010806Sgdamore@opensolaris.org 		mii_resume(hmep->hme_mii);
11919610Sgdamore@opensolaris.org 
11929610Sgdamore@opensolaris.org 		if (hmep->hme_started)
11939610Sgdamore@opensolaris.org 			(void) hmeinit(hmep);
11949610Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
11959610Sgdamore@opensolaris.org 
11969610Sgdamore@opensolaris.org 	default:
11979610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
11989610Sgdamore@opensolaris.org 	}
11999610Sgdamore@opensolaris.org 
12009610Sgdamore@opensolaris.org 	/*
12019610Sgdamore@opensolaris.org 	 * Allocate soft device data structure
12029610Sgdamore@opensolaris.org 	 */
12039610Sgdamore@opensolaris.org 	hmep = kmem_zalloc(sizeof (*hmep), KM_SLEEP);
12049610Sgdamore@opensolaris.org 
12059610Sgdamore@opensolaris.org 	/*
12069610Sgdamore@opensolaris.org 	 * Might as well set up elements of data structure
12079610Sgdamore@opensolaris.org 	 */
12089610Sgdamore@opensolaris.org 	hmep->dip =		dip;
12099610Sgdamore@opensolaris.org 	hmep->instance = 	ddi_get_instance(dip);
12109610Sgdamore@opensolaris.org 	hmep->pagesize =	ddi_ptob(dip, (ulong_t)1); /* IOMMU PSize */
12119610Sgdamore@opensolaris.org 
12129610Sgdamore@opensolaris.org 	/*
12139610Sgdamore@opensolaris.org 	 *  Might as well setup the driver private
12149610Sgdamore@opensolaris.org 	 * structure as part of the dip.
12159610Sgdamore@opensolaris.org 	 */
12169610Sgdamore@opensolaris.org 	ddi_set_driver_private(dip, hmep);
12179610Sgdamore@opensolaris.org 
12189610Sgdamore@opensolaris.org 	/*
12199610Sgdamore@opensolaris.org 	 * Reject this device if it's in a slave-only slot.
12209610Sgdamore@opensolaris.org 	 */
12219610Sgdamore@opensolaris.org 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
12229610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
12239610Sgdamore@opensolaris.org 		    "Dev not used - dev in slave only slot");
12249610Sgdamore@opensolaris.org 		goto error_state;
12259610Sgdamore@opensolaris.org 	}
12269610Sgdamore@opensolaris.org 
12279610Sgdamore@opensolaris.org 	/*
12289610Sgdamore@opensolaris.org 	 * Map in the device registers.
12299610Sgdamore@opensolaris.org 	 *
12309610Sgdamore@opensolaris.org 	 * Reg # 0 is the Global register set
12319610Sgdamore@opensolaris.org 	 * Reg # 1 is the ETX register set
12329610Sgdamore@opensolaris.org 	 * Reg # 2 is the ERX register set
12339610Sgdamore@opensolaris.org 	 * Reg # 3 is the BigMAC register set.
12349610Sgdamore@opensolaris.org 	 * Reg # 4 is the MIF register set
12359610Sgdamore@opensolaris.org 	 */
12369610Sgdamore@opensolaris.org 	if (ddi_dev_nregs(dip, &regno) != (DDI_SUCCESS)) {
12379610Sgdamore@opensolaris.org 		HME_FAULT_MSG2(hmep, SEVERITY_HIGH, INIT_MSG,
12389610Sgdamore@opensolaris.org 		    ddi_nregs_fail_msg, regno);
12399610Sgdamore@opensolaris.org 		goto error_state;
12409610Sgdamore@opensolaris.org 	}
12419610Sgdamore@opensolaris.org 
12429610Sgdamore@opensolaris.org 	switch (regno) {
12439610Sgdamore@opensolaris.org 	case 5:
12449610Sgdamore@opensolaris.org 		hmep->hme_cheerio_mode = 0;
12459610Sgdamore@opensolaris.org 		break;
12469610Sgdamore@opensolaris.org 	case 2:
12479610Sgdamore@opensolaris.org 	case 3: /* for hot swap/plug, there will be 3 entries in "reg" prop */
12489610Sgdamore@opensolaris.org 		hmep->hme_cheerio_mode = 1;
12499610Sgdamore@opensolaris.org 		break;
12509610Sgdamore@opensolaris.org 	default:
12519610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
12529610Sgdamore@opensolaris.org 		    bad_num_regs_msg);
12539610Sgdamore@opensolaris.org 		goto error_state;
12549610Sgdamore@opensolaris.org 	}
12559610Sgdamore@opensolaris.org 
12569610Sgdamore@opensolaris.org 	/* Initialize device attributes structure */
12579610Sgdamore@opensolaris.org 	hmep->hme_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
12589610Sgdamore@opensolaris.org 
12599610Sgdamore@opensolaris.org 	if (hmep->hme_cheerio_mode)
12609610Sgdamore@opensolaris.org 		hmep->hme_dev_attr.devacc_attr_endian_flags =
12619610Sgdamore@opensolaris.org 		    DDI_STRUCTURE_LE_ACC;
12629610Sgdamore@opensolaris.org 	else
12639610Sgdamore@opensolaris.org 		hmep->hme_dev_attr.devacc_attr_endian_flags =
12649610Sgdamore@opensolaris.org 		    DDI_STRUCTURE_BE_ACC;
12659610Sgdamore@opensolaris.org 
12669610Sgdamore@opensolaris.org 	hmep->hme_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
12679610Sgdamore@opensolaris.org 
12689610Sgdamore@opensolaris.org 	if (hmep->hme_cheerio_mode) {
12699610Sgdamore@opensolaris.org 		uint8_t		oldLT;
12709610Sgdamore@opensolaris.org 		uint8_t		newLT = 0;
12719610Sgdamore@opensolaris.org 		dev_info_t	*pdip;
12729610Sgdamore@opensolaris.org 		const char	*pdrvname;
12739610Sgdamore@opensolaris.org 
12749610Sgdamore@opensolaris.org 		/*
12759610Sgdamore@opensolaris.org 		 * Map the PCI config space
12769610Sgdamore@opensolaris.org 		 */
12779610Sgdamore@opensolaris.org 		if (pci_config_setup(dip, &hmep->pci_config_handle) !=
12789610Sgdamore@opensolaris.org 		    DDI_SUCCESS) {
12799610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
12809610Sgdamore@opensolaris.org 			    "pci_config_setup() failed..");
12819610Sgdamore@opensolaris.org 			goto error_state;
12829610Sgdamore@opensolaris.org 		}
12839610Sgdamore@opensolaris.org 
12849610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 1,
12859610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_globregp), 0, 0,
12869610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_globregh)) {
12879610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
12889610Sgdamore@opensolaris.org 			    mregs_4global_reg_fail_msg);
12899610Sgdamore@opensolaris.org 			goto error_unmap;
12909610Sgdamore@opensolaris.org 		}
12919610Sgdamore@opensolaris.org 		hmep->hme_etxregh = hmep->hme_erxregh = hmep->hme_bmacregh =
12929610Sgdamore@opensolaris.org 		    hmep->hme_mifregh = hmep->hme_globregh;
12939610Sgdamore@opensolaris.org 
12949610Sgdamore@opensolaris.org 		hmep->hme_etxregp =
12959610Sgdamore@opensolaris.org 		    (void *)(((caddr_t)hmep->hme_globregp) + 0x2000);
12969610Sgdamore@opensolaris.org 		hmep->hme_erxregp =
12979610Sgdamore@opensolaris.org 		    (void *)(((caddr_t)hmep->hme_globregp) + 0x4000);
12989610Sgdamore@opensolaris.org 		hmep->hme_bmacregp =
12999610Sgdamore@opensolaris.org 		    (void *)(((caddr_t)hmep->hme_globregp) + 0x6000);
13009610Sgdamore@opensolaris.org 		hmep->hme_mifregp =
13019610Sgdamore@opensolaris.org 		    (void *)(((caddr_t)hmep->hme_globregp) + 0x7000);
13029610Sgdamore@opensolaris.org 
13039610Sgdamore@opensolaris.org 		/*
13049610Sgdamore@opensolaris.org 		 * Get parent pci bridge info.
13059610Sgdamore@opensolaris.org 		 */
13069610Sgdamore@opensolaris.org 		pdip = ddi_get_parent(dip);
13079610Sgdamore@opensolaris.org 		pdrvname = ddi_driver_name(pdip);
13089610Sgdamore@opensolaris.org 
13099610Sgdamore@opensolaris.org 		oldLT = pci_config_get8(hmep->pci_config_handle,
13109610Sgdamore@opensolaris.org 		    PCI_CONF_LATENCY_TIMER);
13119610Sgdamore@opensolaris.org 		/*
13129610Sgdamore@opensolaris.org 		 * Honor value set in /etc/system
13139610Sgdamore@opensolaris.org 		 * "set hme:pci_latency_timer=0xYY"
13149610Sgdamore@opensolaris.org 		 */
13159610Sgdamore@opensolaris.org 		if (pci_latency_timer)
13169610Sgdamore@opensolaris.org 			newLT = pci_latency_timer;
13179610Sgdamore@opensolaris.org 		/*
13189610Sgdamore@opensolaris.org 		 * Modify LT for simba
13199610Sgdamore@opensolaris.org 		 */
13209610Sgdamore@opensolaris.org 		else if (strcmp("simba", pdrvname) == 0)
13219610Sgdamore@opensolaris.org 			newLT = 0xf0;
13229610Sgdamore@opensolaris.org 		/*
13239610Sgdamore@opensolaris.org 		 * Ensure minimum cheerio latency timer of 0x50
13249610Sgdamore@opensolaris.org 		 * Usually OBP or pci bridge should set this value
13259610Sgdamore@opensolaris.org 		 * based on cheerio
13269610Sgdamore@opensolaris.org 		 * min_grant * 8(33MHz) = 0x50 = 0xa * 0x8
13279610Sgdamore@opensolaris.org 		 * Some system set cheerio LT at 0x40
13289610Sgdamore@opensolaris.org 		 */
13299610Sgdamore@opensolaris.org 		else if (oldLT < 0x40)
13309610Sgdamore@opensolaris.org 			newLT = 0x50;
13319610Sgdamore@opensolaris.org 
13329610Sgdamore@opensolaris.org 		/*
13339610Sgdamore@opensolaris.org 		 * Now program cheerio's pci latency timer with newLT
13349610Sgdamore@opensolaris.org 		 */
13359610Sgdamore@opensolaris.org 		if (newLT)
13369610Sgdamore@opensolaris.org 			pci_config_put8(hmep->pci_config_handle,
13379610Sgdamore@opensolaris.org 			    PCI_CONF_LATENCY_TIMER, (uchar_t)newLT);
13389610Sgdamore@opensolaris.org 	} else { /* Map register sets */
13399610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 0,
13409610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_globregp), 0, 0,
13419610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_globregh)) {
13429610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
13439610Sgdamore@opensolaris.org 			    mregs_4global_reg_fail_msg);
13449610Sgdamore@opensolaris.org 			goto error_state;
13459610Sgdamore@opensolaris.org 		}
13469610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 1,
13479610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_etxregp), 0, 0,
13489610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_etxregh)) {
13499610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
13509610Sgdamore@opensolaris.org 			    mregs_4etx_reg_fail_msg);
13519610Sgdamore@opensolaris.org 			goto error_unmap;
13529610Sgdamore@opensolaris.org 		}
13539610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 2,
13549610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_erxregp), 0, 0,
13559610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_erxregh)) {
13569610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
13579610Sgdamore@opensolaris.org 			    mregs_4erx_reg_fail_msg);
13589610Sgdamore@opensolaris.org 			goto error_unmap;
13599610Sgdamore@opensolaris.org 		}
13609610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 3,
13619610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_bmacregp), 0, 0,
13629610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_bmacregh)) {
13639610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
13649610Sgdamore@opensolaris.org 			    mregs_4bmac_reg_fail_msg);
13659610Sgdamore@opensolaris.org 			goto error_unmap;
13669610Sgdamore@opensolaris.org 		}
13679610Sgdamore@opensolaris.org 
13689610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(dip, 4,
13699610Sgdamore@opensolaris.org 		    (caddr_t *)&(hmep->hme_mifregp), 0, 0,
13709610Sgdamore@opensolaris.org 		    &hmep->hme_dev_attr, &hmep->hme_mifregh)) {
13719610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
13729610Sgdamore@opensolaris.org 			    mregs_4mif_reg_fail_msg);
13739610Sgdamore@opensolaris.org 			goto error_unmap;
13749610Sgdamore@opensolaris.org 		}
13759610Sgdamore@opensolaris.org 	} /* Endif cheerio_mode */
13769610Sgdamore@opensolaris.org 
13779610Sgdamore@opensolaris.org 	/*
13789610Sgdamore@opensolaris.org 	 * Based on the hm-rev, set some capabilities
13799610Sgdamore@opensolaris.org 	 * Set up default capabilities for HM 2.0
13809610Sgdamore@opensolaris.org 	 */
13819610Sgdamore@opensolaris.org 	hmep->hme_frame_enable = 0;
13829610Sgdamore@opensolaris.org 	hmep->hme_lance_mode_enable = 0;
13839610Sgdamore@opensolaris.org 	hmep->hme_rxcv_enable = 0;
13849610Sgdamore@opensolaris.org 
13859610Sgdamore@opensolaris.org 	/* NEW routine to get the properties */
13869610Sgdamore@opensolaris.org 
13879610Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, hmep->dip, 0, "hm-rev",
13889610Sgdamore@opensolaris.org 	    (caddr_t)&hm_rev, &prop_len) == DDI_PROP_SUCCESS) {
13899610Sgdamore@opensolaris.org 
13909610Sgdamore@opensolaris.org 		hmep->asic_rev = hm_rev;
13919610Sgdamore@opensolaris.org 		hmeget_hm_rev_property(hmep);
13929610Sgdamore@opensolaris.org 	} else {
13939610Sgdamore@opensolaris.org 		/*
13949610Sgdamore@opensolaris.org 		 * hm_rev property not found so, this is
13959610Sgdamore@opensolaris.org 		 * case of hot insertion of card without interpreting fcode.
13969610Sgdamore@opensolaris.org 		 * Get it from revid in config space after mapping it.
13979610Sgdamore@opensolaris.org 		 */
13989610Sgdamore@opensolaris.org 		if (ddi_regs_map_setup(hmep->dip, 0, (caddr_t *)&cfg_ptr,
13999610Sgdamore@opensolaris.org 		    0, 0, &hmep->hme_dev_attr, &cfg_handle)) {
14009610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
14019610Sgdamore@opensolaris.org 		}
14029610Sgdamore@opensolaris.org 		/*
14039610Sgdamore@opensolaris.org 		 * Since this is cheerio-based PCI card, we write 0xC in the
14049610Sgdamore@opensolaris.org 		 * top 4 bits(4-7) of hm-rev and retain the bottom(0-3) bits
14059610Sgdamore@opensolaris.org 		 * for Cheerio version(1.0 or 2.0 = 0xC0 or 0xC1)
14069610Sgdamore@opensolaris.org 		 */
14079610Sgdamore@opensolaris.org 		hm_rev = ddi_get8(cfg_handle, &cfg_ptr->revid);
14089610Sgdamore@opensolaris.org 		hm_rev = HME_1C0_REVID | (hm_rev & HME_REV_VERS_MASK);
14099610Sgdamore@opensolaris.org 		hmep->asic_rev = hm_rev;
14109610Sgdamore@opensolaris.org 		if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
14119610Sgdamore@opensolaris.org 		    "hm-rev", (caddr_t)&hm_rev, sizeof (hm_rev)) !=
14129610Sgdamore@opensolaris.org 		    DDI_SUCCESS) {
14139610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, AUTOCONFIG_MSG,
141410806Sgdamore@opensolaris.org 			    "ddi_prop_create error for hm_rev");
14159610Sgdamore@opensolaris.org 		}
14169610Sgdamore@opensolaris.org 		ddi_regs_map_free(&cfg_handle);
14179610Sgdamore@opensolaris.org 
14189610Sgdamore@opensolaris.org 		hmeget_hm_rev_property(hmep);
14199610Sgdamore@opensolaris.org 
14209610Sgdamore@opensolaris.org 		/* get info via VPD */
14219610Sgdamore@opensolaris.org 		if (hmeget_promprops(dip) != DDI_SUCCESS) {
14229610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, AUTOCONFIG_MSG,
142310806Sgdamore@opensolaris.org 			    "no promprops");
14249610Sgdamore@opensolaris.org 		}
14259610Sgdamore@opensolaris.org 	}
14269610Sgdamore@opensolaris.org 
14279610Sgdamore@opensolaris.org 	if (ddi_intr_hilevel(dip, 0)) {
14289610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, NFATAL_ERR_MSG,
14299610Sgdamore@opensolaris.org 		    " high-level interrupts are not supported");
14309610Sgdamore@opensolaris.org 		goto error_unmap;
14319610Sgdamore@opensolaris.org 	}
14329610Sgdamore@opensolaris.org 
14339610Sgdamore@opensolaris.org 	/*
14349610Sgdamore@opensolaris.org 	 * Get intr. block cookie so that mutex locks can be initialized.
14359610Sgdamore@opensolaris.org 	 */
14369610Sgdamore@opensolaris.org 	if (ddi_get_iblock_cookie(dip, 0, &hmep->hme_cookie) != DDI_SUCCESS)
14379610Sgdamore@opensolaris.org 		goto error_unmap;
14389610Sgdamore@opensolaris.org 
14399610Sgdamore@opensolaris.org 	/*
14409610Sgdamore@opensolaris.org 	 * Initialize mutex's for this device.
14419610Sgdamore@opensolaris.org 	 */
14429610Sgdamore@opensolaris.org 	mutex_init(&hmep->hme_xmitlock, NULL, MUTEX_DRIVER, hmep->hme_cookie);
14439610Sgdamore@opensolaris.org 	mutex_init(&hmep->hme_intrlock, NULL, MUTEX_DRIVER, hmep->hme_cookie);
14449610Sgdamore@opensolaris.org 
14459610Sgdamore@opensolaris.org 	/*
14469610Sgdamore@opensolaris.org 	 * Quiesce the hardware.
14479610Sgdamore@opensolaris.org 	 */
14489610Sgdamore@opensolaris.org 	(void) hmestop(hmep);
14499610Sgdamore@opensolaris.org 
14509610Sgdamore@opensolaris.org 	/*
14519610Sgdamore@opensolaris.org 	 * Add interrupt to system
14529610Sgdamore@opensolaris.org 	 */
14539610Sgdamore@opensolaris.org 	if (ddi_add_intr(dip, 0, (ddi_iblock_cookie_t *)NULL,
14549610Sgdamore@opensolaris.org 	    (ddi_idevice_cookie_t *)NULL, hmeintr, (caddr_t)hmep)) {
14559610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, CONFIG_MSG,
14569610Sgdamore@opensolaris.org 		    add_intr_fail_msg);
14579610Sgdamore@opensolaris.org 		goto error_mutex;
14589610Sgdamore@opensolaris.org 	}
14599610Sgdamore@opensolaris.org 
14609610Sgdamore@opensolaris.org 	/*
14619610Sgdamore@opensolaris.org 	 * Set up the ethernet mac address.
14629610Sgdamore@opensolaris.org 	 */
14639610Sgdamore@opensolaris.org 	hme_setup_mac_address(hmep, dip);
14649610Sgdamore@opensolaris.org 
14659610Sgdamore@opensolaris.org 	if (!hmeinit_xfer_params(hmep))
14669610Sgdamore@opensolaris.org 		goto error_intr;
14679610Sgdamore@opensolaris.org 
14689610Sgdamore@opensolaris.org 	if (hmeburstsizes(hmep) == DDI_FAILURE) {
14699610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG, burst_size_msg);
14709610Sgdamore@opensolaris.org 		goto error_intr;
14719610Sgdamore@opensolaris.org 	}
14729610Sgdamore@opensolaris.org 
14739610Sgdamore@opensolaris.org 	if (hmeallocthings(hmep) != DDI_SUCCESS) {
14749610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, CONFIG_MSG,
14759610Sgdamore@opensolaris.org 		    "resource allocation failed");
14769610Sgdamore@opensolaris.org 		goto error_intr;
14779610Sgdamore@opensolaris.org 	}
14789610Sgdamore@opensolaris.org 
14799610Sgdamore@opensolaris.org 	if (hmeallocbufs(hmep) != DDI_SUCCESS) {
14809610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, CONFIG_MSG,
14819610Sgdamore@opensolaris.org 		    "buffer allocation failed");
14829610Sgdamore@opensolaris.org 		goto error_intr;
14839610Sgdamore@opensolaris.org 	}
14849610Sgdamore@opensolaris.org 
14859610Sgdamore@opensolaris.org 	hmestatinit(hmep);
14869610Sgdamore@opensolaris.org 
148711419Sgdamore@opensolaris.org 	/* our external (preferred) PHY is at address 0 */
148811419Sgdamore@opensolaris.org 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, "first-phy", 0);
148911419Sgdamore@opensolaris.org 
149010806Sgdamore@opensolaris.org 	hmep->hme_mii = mii_alloc(hmep, dip, &hme_mii_ops);
149110806Sgdamore@opensolaris.org 	if (hmep->hme_mii == NULL) {
149210806Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, CONFIG_MSG,
149310806Sgdamore@opensolaris.org 		    "mii_alloc failed");
149410806Sgdamore@opensolaris.org 		goto error_intr;
149510806Sgdamore@opensolaris.org 	}
149610806Sgdamore@opensolaris.org 	/* force a probe for the PHY */
149710806Sgdamore@opensolaris.org 	mii_probe(hmep->hme_mii);
149810806Sgdamore@opensolaris.org 
14999610Sgdamore@opensolaris.org 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
15009610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, CONFIG_MSG,
15019610Sgdamore@opensolaris.org 		    "mac_alloc failed");
15029610Sgdamore@opensolaris.org 		goto error_intr;
15039610Sgdamore@opensolaris.org 	}
15049610Sgdamore@opensolaris.org 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
15059610Sgdamore@opensolaris.org 	macp->m_driver = hmep;
15069610Sgdamore@opensolaris.org 	macp->m_dip = dip;
15079610Sgdamore@opensolaris.org 	macp->m_src_addr = hmep->hme_ouraddr.ether_addr_octet;
15089610Sgdamore@opensolaris.org 	macp->m_callbacks = &hme_m_callbacks;
15099610Sgdamore@opensolaris.org 	macp->m_min_sdu = 0;
15109610Sgdamore@opensolaris.org 	macp->m_max_sdu = ETHERMTU;
15119610Sgdamore@opensolaris.org 	macp->m_margin = VLAN_TAGSZ;
151210806Sgdamore@opensolaris.org 	macp->m_priv_props = hme_priv_prop;
15139610Sgdamore@opensolaris.org 	if (mac_register(macp, &hmep->hme_mh) != 0) {
15149610Sgdamore@opensolaris.org 		mac_free(macp);
15159610Sgdamore@opensolaris.org 		goto error_intr;
15169610Sgdamore@opensolaris.org 	}
15179610Sgdamore@opensolaris.org 
15189610Sgdamore@opensolaris.org 	mac_free(macp);
15199610Sgdamore@opensolaris.org 
15209610Sgdamore@opensolaris.org 	ddi_report_dev(dip);
15219610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
15229610Sgdamore@opensolaris.org 
15239610Sgdamore@opensolaris.org 	/*
15249610Sgdamore@opensolaris.org 	 * Failure Exit
15259610Sgdamore@opensolaris.org 	 */
15269610Sgdamore@opensolaris.org 
15279610Sgdamore@opensolaris.org error_intr:
15289610Sgdamore@opensolaris.org 	if (hmep->hme_cookie)
15299610Sgdamore@opensolaris.org 		ddi_remove_intr(dip, 0, (ddi_iblock_cookie_t)0);
15309610Sgdamore@opensolaris.org 
153110806Sgdamore@opensolaris.org 	if (hmep->hme_mii)
153210806Sgdamore@opensolaris.org 		mii_free(hmep->hme_mii);
153310806Sgdamore@opensolaris.org 
15349610Sgdamore@opensolaris.org error_mutex:
15359610Sgdamore@opensolaris.org 	mutex_destroy(&hmep->hme_xmitlock);
15369610Sgdamore@opensolaris.org 	mutex_destroy(&hmep->hme_intrlock);
15379610Sgdamore@opensolaris.org 
15389610Sgdamore@opensolaris.org error_unmap:
15399610Sgdamore@opensolaris.org 	if (hmep->hme_globregh)
15409610Sgdamore@opensolaris.org 		ddi_regs_map_free(&hmep->hme_globregh);
15419610Sgdamore@opensolaris.org 	if (hmep->hme_cheerio_mode == 0) {
15429610Sgdamore@opensolaris.org 		if (hmep->hme_etxregh)
15439610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_etxregh);
15449610Sgdamore@opensolaris.org 		if (hmep->hme_erxregh)
15459610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_erxregh);
15469610Sgdamore@opensolaris.org 		if (hmep->hme_bmacregh)
15479610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_bmacregh);
15489610Sgdamore@opensolaris.org 		if (hmep->hme_mifregh)
15499610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_mifregh);
15509610Sgdamore@opensolaris.org 	} else {
15519610Sgdamore@opensolaris.org 		if (hmep->pci_config_handle)
15529610Sgdamore@opensolaris.org 			(void) pci_config_teardown(&hmep->pci_config_handle);
15539610Sgdamore@opensolaris.org 		hmep->hme_etxregh = hmep->hme_erxregh = hmep->hme_bmacregh =
15549610Sgdamore@opensolaris.org 		    hmep->hme_mifregh = hmep->hme_globregh = NULL;
15559610Sgdamore@opensolaris.org 	}
15569610Sgdamore@opensolaris.org 
15579610Sgdamore@opensolaris.org error_state:
15589610Sgdamore@opensolaris.org 	hmefreethings(hmep);
15599610Sgdamore@opensolaris.org 	hmefreebufs(hmep);
15609610Sgdamore@opensolaris.org 
15619610Sgdamore@opensolaris.org 	if (hmep) {
15629610Sgdamore@opensolaris.org 		kmem_free((caddr_t)hmep, sizeof (*hmep));
15639610Sgdamore@opensolaris.org 		ddi_set_driver_private(dip, NULL);
15649610Sgdamore@opensolaris.org 	}
15659610Sgdamore@opensolaris.org 
15669610Sgdamore@opensolaris.org 	return (DDI_FAILURE);
15679610Sgdamore@opensolaris.org }
15689610Sgdamore@opensolaris.org 
15699610Sgdamore@opensolaris.org int
hmedetach(dev_info_t * dip,ddi_detach_cmd_t cmd)15709610Sgdamore@opensolaris.org hmedetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
15719610Sgdamore@opensolaris.org {
15729610Sgdamore@opensolaris.org 	struct hme *hmep;
15739610Sgdamore@opensolaris.org 
15749610Sgdamore@opensolaris.org 	if ((hmep = ddi_get_driver_private(dip)) == NULL)
15759610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
15769610Sgdamore@opensolaris.org 
15779610Sgdamore@opensolaris.org 	switch (cmd) {
15789610Sgdamore@opensolaris.org 	case DDI_DETACH:
15799610Sgdamore@opensolaris.org 		break;
15809610Sgdamore@opensolaris.org 
15819610Sgdamore@opensolaris.org 	case DDI_SUSPEND:
158210806Sgdamore@opensolaris.org 		mii_suspend(hmep->hme_mii);
15839610Sgdamore@opensolaris.org 		hmep->hme_flags |= HMESUSPENDED;
15849610Sgdamore@opensolaris.org 		hmeuninit(hmep);
15859610Sgdamore@opensolaris.org 		return (DDI_SUCCESS);
15869610Sgdamore@opensolaris.org 
15879610Sgdamore@opensolaris.org 	default:
15889610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
15899610Sgdamore@opensolaris.org 	}
15909610Sgdamore@opensolaris.org 
15919610Sgdamore@opensolaris.org 
15929610Sgdamore@opensolaris.org 	if (mac_unregister(hmep->hme_mh) != 0) {
15939610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
15949610Sgdamore@opensolaris.org 	}
15959610Sgdamore@opensolaris.org 
15969610Sgdamore@opensolaris.org 	/*
15979610Sgdamore@opensolaris.org 	 * Make driver quiescent, we don't want to prevent the
15989610Sgdamore@opensolaris.org 	 * detach on failure.  Note that this should be redundant,
15999610Sgdamore@opensolaris.org 	 * since mac_stop should already have called hmeuninit().
16009610Sgdamore@opensolaris.org 	 */
16019610Sgdamore@opensolaris.org 	if (!(hmep->hme_flags & HMESUSPENDED)) {
16029610Sgdamore@opensolaris.org 		(void) hmestop(hmep);
16039610Sgdamore@opensolaris.org 	}
16049610Sgdamore@opensolaris.org 
160510806Sgdamore@opensolaris.org 	if (hmep->hme_mii)
160610806Sgdamore@opensolaris.org 		mii_free(hmep->hme_mii);
160710806Sgdamore@opensolaris.org 
16089610Sgdamore@opensolaris.org 	/*
16099610Sgdamore@opensolaris.org 	 * Remove instance of the intr
16109610Sgdamore@opensolaris.org 	 */
16119610Sgdamore@opensolaris.org 	ddi_remove_intr(dip, 0, (ddi_iblock_cookie_t)0);
16129610Sgdamore@opensolaris.org 
16139610Sgdamore@opensolaris.org 	/*
16149610Sgdamore@opensolaris.org 	 * Unregister kstats.
16159610Sgdamore@opensolaris.org 	 */
16169610Sgdamore@opensolaris.org 	if (hmep->hme_ksp != NULL)
16179610Sgdamore@opensolaris.org 		kstat_delete(hmep->hme_ksp);
16189610Sgdamore@opensolaris.org 	if (hmep->hme_intrstats != NULL)
16199610Sgdamore@opensolaris.org 		kstat_delete(hmep->hme_intrstats);
16209610Sgdamore@opensolaris.org 
16219610Sgdamore@opensolaris.org 	hmep->hme_ksp = NULL;
16229610Sgdamore@opensolaris.org 	hmep->hme_intrstats = NULL;
16239610Sgdamore@opensolaris.org 
16249610Sgdamore@opensolaris.org 	/*
16259610Sgdamore@opensolaris.org 	 * Destroy all mutexes and data structures allocated during
16269610Sgdamore@opensolaris.org 	 * attach time.
16279610Sgdamore@opensolaris.org 	 *
16289610Sgdamore@opensolaris.org 	 * Note: at this time we should be the only thread accessing
16299610Sgdamore@opensolaris.org 	 * the structures for this instance.
16309610Sgdamore@opensolaris.org 	 */
16319610Sgdamore@opensolaris.org 
16329610Sgdamore@opensolaris.org 	if (hmep->hme_globregh)
16339610Sgdamore@opensolaris.org 		ddi_regs_map_free(&hmep->hme_globregh);
16349610Sgdamore@opensolaris.org 	if (hmep->hme_cheerio_mode == 0) {
16359610Sgdamore@opensolaris.org 		if (hmep->hme_etxregh)
16369610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_etxregh);
16379610Sgdamore@opensolaris.org 		if (hmep->hme_erxregh)
16389610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_erxregh);
16399610Sgdamore@opensolaris.org 		if (hmep->hme_bmacregh)
16409610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_bmacregh);
16419610Sgdamore@opensolaris.org 		if (hmep->hme_mifregh)
16429610Sgdamore@opensolaris.org 			ddi_regs_map_free(&hmep->hme_mifregh);
16439610Sgdamore@opensolaris.org 	} else {
16449610Sgdamore@opensolaris.org 		if (hmep->pci_config_handle)
16459610Sgdamore@opensolaris.org 			(void) pci_config_teardown(&hmep->pci_config_handle);
16469610Sgdamore@opensolaris.org 		hmep->hme_etxregh = hmep->hme_erxregh = hmep->hme_bmacregh =
16479610Sgdamore@opensolaris.org 		    hmep->hme_mifregh = hmep->hme_globregh = NULL;
16489610Sgdamore@opensolaris.org 	}
16499610Sgdamore@opensolaris.org 
16509610Sgdamore@opensolaris.org 	mutex_destroy(&hmep->hme_xmitlock);
16519610Sgdamore@opensolaris.org 	mutex_destroy(&hmep->hme_intrlock);
16529610Sgdamore@opensolaris.org 
16539610Sgdamore@opensolaris.org 	hmefreethings(hmep);
16549610Sgdamore@opensolaris.org 	hmefreebufs(hmep);
16559610Sgdamore@opensolaris.org 
16569610Sgdamore@opensolaris.org 	ddi_set_driver_private(dip, NULL);
16579610Sgdamore@opensolaris.org 	kmem_free(hmep, sizeof (struct hme));
16589610Sgdamore@opensolaris.org 
16599610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
16609610Sgdamore@opensolaris.org }
16619610Sgdamore@opensolaris.org 
16629610Sgdamore@opensolaris.org int
hmequiesce(dev_info_t * dip)16639610Sgdamore@opensolaris.org hmequiesce(dev_info_t *dip)
16649610Sgdamore@opensolaris.org {
16659610Sgdamore@opensolaris.org 	struct hme *hmep;
16669610Sgdamore@opensolaris.org 
16679610Sgdamore@opensolaris.org 	if ((hmep = ddi_get_driver_private(dip)) == NULL)
16689610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
16699610Sgdamore@opensolaris.org 
16709610Sgdamore@opensolaris.org 	(void) hmestop(hmep);
16719610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
16729610Sgdamore@opensolaris.org }
16739610Sgdamore@opensolaris.org 
16749610Sgdamore@opensolaris.org static boolean_t
hmeinit_xfer_params(struct hme * hmep)16759610Sgdamore@opensolaris.org hmeinit_xfer_params(struct hme *hmep)
16769610Sgdamore@opensolaris.org {
16779610Sgdamore@opensolaris.org 	int hme_ipg1_conf, hme_ipg2_conf;
16789610Sgdamore@opensolaris.org 	int hme_ipg0_conf, hme_lance_mode_conf;
16799610Sgdamore@opensolaris.org 	int prop_len = sizeof (int);
16809610Sgdamore@opensolaris.org 	dev_info_t *dip;
16819610Sgdamore@opensolaris.org 
16829610Sgdamore@opensolaris.org 	dip = hmep->dip;
16839610Sgdamore@opensolaris.org 
16849610Sgdamore@opensolaris.org 	/*
16859610Sgdamore@opensolaris.org 	 * Set up the start-up values for user-configurable parameters
16869610Sgdamore@opensolaris.org 	 * Get the values from the global variables first.
16879610Sgdamore@opensolaris.org 	 * Use the MASK to limit the value to allowed maximum.
16889610Sgdamore@opensolaris.org 	 */
168910806Sgdamore@opensolaris.org 	hmep->hme_ipg1 = hme_ipg1 & HME_MASK_8BIT;
169010806Sgdamore@opensolaris.org 	hmep->hme_ipg2 = hme_ipg2 & HME_MASK_8BIT;
169110806Sgdamore@opensolaris.org 	hmep->hme_ipg0 = hme_ipg0 & HME_MASK_5BIT;
16929610Sgdamore@opensolaris.org 
16939610Sgdamore@opensolaris.org 	/*
16949610Sgdamore@opensolaris.org 	 * Get the parameter values configured in .conf file.
16959610Sgdamore@opensolaris.org 	 */
16969610Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "ipg1",
16979610Sgdamore@opensolaris.org 	    (caddr_t)&hme_ipg1_conf, &prop_len) == DDI_PROP_SUCCESS) {
169810806Sgdamore@opensolaris.org 		hmep->hme_ipg1 = hme_ipg1_conf & HME_MASK_8BIT;
16999610Sgdamore@opensolaris.org 	}
17009610Sgdamore@opensolaris.org 
17019610Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "ipg2",
17029610Sgdamore@opensolaris.org 	    (caddr_t)&hme_ipg2_conf, &prop_len) == DDI_PROP_SUCCESS) {
170310806Sgdamore@opensolaris.org 		hmep->hme_ipg2 = hme_ipg2_conf & HME_MASK_8BIT;
17049610Sgdamore@opensolaris.org 	}
17059610Sgdamore@opensolaris.org 
17069610Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "ipg0",
17079610Sgdamore@opensolaris.org 	    (caddr_t)&hme_ipg0_conf, &prop_len) == DDI_PROP_SUCCESS) {
170810806Sgdamore@opensolaris.org 		hmep->hme_ipg0 = hme_ipg0_conf & HME_MASK_5BIT;
17099610Sgdamore@opensolaris.org 	}
17109610Sgdamore@opensolaris.org 
17119610Sgdamore@opensolaris.org 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "lance_mode",
17129610Sgdamore@opensolaris.org 	    (caddr_t)&hme_lance_mode_conf, &prop_len) == DDI_PROP_SUCCESS) {
171310806Sgdamore@opensolaris.org 		hmep->hme_lance_mode = hme_lance_mode_conf & HME_MASK_1BIT;
17149610Sgdamore@opensolaris.org 	}
17159610Sgdamore@opensolaris.org 
17169610Sgdamore@opensolaris.org 	return (B_TRUE);
17179610Sgdamore@opensolaris.org }
17189610Sgdamore@opensolaris.org 
17199610Sgdamore@opensolaris.org /*
17209610Sgdamore@opensolaris.org  * Return 0 upon success, 1 on failure.
17219610Sgdamore@opensolaris.org  */
17229610Sgdamore@opensolaris.org static uint_t
hmestop(struct hme * hmep)17239610Sgdamore@opensolaris.org hmestop(struct hme *hmep)
17249610Sgdamore@opensolaris.org {
17259610Sgdamore@opensolaris.org 	/*
17269610Sgdamore@opensolaris.org 	 * Disable the Tx dma engine.
17279610Sgdamore@opensolaris.org 	 */
17289610Sgdamore@opensolaris.org 	PUT_ETXREG(config, (GET_ETXREG(config) & ~HMET_CONFIG_TXDMA_EN));
17299610Sgdamore@opensolaris.org 	HMEDELAY(((GET_ETXREG(state_mach) & 0x1f) == 0x1), HMEMAXRSTDELAY);
17309610Sgdamore@opensolaris.org 
17319610Sgdamore@opensolaris.org 	/*
17329610Sgdamore@opensolaris.org 	 * Disable the Rx dma engine.
17339610Sgdamore@opensolaris.org 	 */
17349610Sgdamore@opensolaris.org 	PUT_ERXREG(config, (GET_ERXREG(config) & ~HMER_CONFIG_RXDMA_EN));
17359610Sgdamore@opensolaris.org 	HMEDELAY(((GET_ERXREG(state_mach) & 0x3f) == 0), HMEMAXRSTDELAY);
17369610Sgdamore@opensolaris.org 
17379610Sgdamore@opensolaris.org 	/*
17389610Sgdamore@opensolaris.org 	 * By this time all things should be quiet, so hit the
17399610Sgdamore@opensolaris.org 	 * chip with a reset.
17409610Sgdamore@opensolaris.org 	 */
17419610Sgdamore@opensolaris.org 	PUT_GLOBREG(reset, HMEG_RESET_GLOBAL);
17429610Sgdamore@opensolaris.org 
17439610Sgdamore@opensolaris.org 	HMEDELAY((GET_GLOBREG(reset) == 0), HMEMAXRSTDELAY);
17449610Sgdamore@opensolaris.org 	if (GET_GLOBREG(reset)) {
17459610Sgdamore@opensolaris.org 		return (1);
17469610Sgdamore@opensolaris.org 	}
17479610Sgdamore@opensolaris.org 
17489610Sgdamore@opensolaris.org 	CHECK_GLOBREG();
17499610Sgdamore@opensolaris.org 	return (0);
17509610Sgdamore@opensolaris.org }
17519610Sgdamore@opensolaris.org 
17529610Sgdamore@opensolaris.org static int
hmestat_kstat_update(kstat_t * ksp,int rw)17539610Sgdamore@opensolaris.org hmestat_kstat_update(kstat_t *ksp, int rw)
17549610Sgdamore@opensolaris.org {
17559610Sgdamore@opensolaris.org 	struct hme *hmep;
17569610Sgdamore@opensolaris.org 	struct hmekstat *hkp;
17579610Sgdamore@opensolaris.org 
17589610Sgdamore@opensolaris.org 	hmep = (struct hme *)ksp->ks_private;
17599610Sgdamore@opensolaris.org 	hkp = (struct hmekstat *)ksp->ks_data;
17609610Sgdamore@opensolaris.org 
17619610Sgdamore@opensolaris.org 	if (rw != KSTAT_READ)
17629610Sgdamore@opensolaris.org 		return (EACCES);
17639610Sgdamore@opensolaris.org 
17649610Sgdamore@opensolaris.org 	/*
17659610Sgdamore@opensolaris.org 	 * Update all the stats by reading all the counter registers.
17669610Sgdamore@opensolaris.org 	 * Counter register stats are not updated till they overflow
17679610Sgdamore@opensolaris.org 	 * and interrupt.
17689610Sgdamore@opensolaris.org 	 */
17699610Sgdamore@opensolaris.org 
17709610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_xmitlock);
17719610Sgdamore@opensolaris.org 	if (hmep->hme_flags & HMERUNNING) {
17729610Sgdamore@opensolaris.org 		hmereclaim(hmep);
17739610Sgdamore@opensolaris.org 		hmesavecntrs(hmep);
17749610Sgdamore@opensolaris.org 	}
17759610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
17769610Sgdamore@opensolaris.org 
17779610Sgdamore@opensolaris.org 	hkp->hk_cvc.value.ul		= hmep->hme_cvc;
17789610Sgdamore@opensolaris.org 	hkp->hk_lenerr.value.ul		= hmep->hme_lenerr;
17799610Sgdamore@opensolaris.org 	hkp->hk_buff.value.ul		= hmep->hme_buff;
17809610Sgdamore@opensolaris.org 	hkp->hk_missed.value.ul		= hmep->hme_missed;
17819610Sgdamore@opensolaris.org 	hkp->hk_allocbfail.value.ul	= hmep->hme_allocbfail;
17829610Sgdamore@opensolaris.org 	hkp->hk_babl.value.ul		= hmep->hme_babl;
17839610Sgdamore@opensolaris.org 	hkp->hk_tmder.value.ul		= hmep->hme_tmder;
17849610Sgdamore@opensolaris.org 	hkp->hk_txlaterr.value.ul	= hmep->hme_txlaterr;
17859610Sgdamore@opensolaris.org 	hkp->hk_rxlaterr.value.ul	= hmep->hme_rxlaterr;
17869610Sgdamore@opensolaris.org 	hkp->hk_slvparerr.value.ul	= hmep->hme_slvparerr;
17879610Sgdamore@opensolaris.org 	hkp->hk_txparerr.value.ul	= hmep->hme_txparerr;
17889610Sgdamore@opensolaris.org 	hkp->hk_rxparerr.value.ul	= hmep->hme_rxparerr;
17899610Sgdamore@opensolaris.org 	hkp->hk_slverrack.value.ul	= hmep->hme_slverrack;
17909610Sgdamore@opensolaris.org 	hkp->hk_txerrack.value.ul	= hmep->hme_txerrack;
17919610Sgdamore@opensolaris.org 	hkp->hk_rxerrack.value.ul	= hmep->hme_rxerrack;
17929610Sgdamore@opensolaris.org 	hkp->hk_txtagerr.value.ul	= hmep->hme_txtagerr;
17939610Sgdamore@opensolaris.org 	hkp->hk_rxtagerr.value.ul	= hmep->hme_rxtagerr;
17949610Sgdamore@opensolaris.org 	hkp->hk_eoperr.value.ul		= hmep->hme_eoperr;
17959610Sgdamore@opensolaris.org 	hkp->hk_notmds.value.ul		= hmep->hme_notmds;
17969610Sgdamore@opensolaris.org 	hkp->hk_notbufs.value.ul	= hmep->hme_notbufs;
17979610Sgdamore@opensolaris.org 	hkp->hk_norbufs.value.ul	= hmep->hme_norbufs;
17989610Sgdamore@opensolaris.org 
17999610Sgdamore@opensolaris.org 	/*
18009610Sgdamore@opensolaris.org 	 * Debug kstats
18019610Sgdamore@opensolaris.org 	 */
18029610Sgdamore@opensolaris.org 	hkp->hk_inits.value.ul		= hmep->inits;
18039610Sgdamore@opensolaris.org 	hkp->hk_phyfail.value.ul	= hmep->phyfail;
18049610Sgdamore@opensolaris.org 
18059610Sgdamore@opensolaris.org 	/*
18069610Sgdamore@opensolaris.org 	 * xcvr kstats
18079610Sgdamore@opensolaris.org 	 */
18089610Sgdamore@opensolaris.org 	hkp->hk_asic_rev.value.ul	= hmep->asic_rev;
18099610Sgdamore@opensolaris.org 
18109610Sgdamore@opensolaris.org 	return (0);
18119610Sgdamore@opensolaris.org }
18129610Sgdamore@opensolaris.org 
18139610Sgdamore@opensolaris.org static void
hmestatinit(struct hme * hmep)18149610Sgdamore@opensolaris.org hmestatinit(struct hme *hmep)
18159610Sgdamore@opensolaris.org {
18169610Sgdamore@opensolaris.org 	struct	kstat	*ksp;
18179610Sgdamore@opensolaris.org 	struct	hmekstat	*hkp;
18189610Sgdamore@opensolaris.org 	const char *driver;
18199610Sgdamore@opensolaris.org 	int	instance;
18209610Sgdamore@opensolaris.org 	char	buf[16];
18219610Sgdamore@opensolaris.org 
18229610Sgdamore@opensolaris.org 	instance = hmep->instance;
18239610Sgdamore@opensolaris.org 	driver = ddi_driver_name(hmep->dip);
18249610Sgdamore@opensolaris.org 
18259610Sgdamore@opensolaris.org 	if ((ksp = kstat_create(driver, instance,
18269610Sgdamore@opensolaris.org 	    "driver_info", "net", KSTAT_TYPE_NAMED,
18279610Sgdamore@opensolaris.org 	    sizeof (struct hmekstat) / sizeof (kstat_named_t), 0)) == NULL) {
18289610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_UNKNOWN, INIT_MSG,
18299610Sgdamore@opensolaris.org 		    "kstat_create failed");
18309610Sgdamore@opensolaris.org 		return;
18319610Sgdamore@opensolaris.org 	}
18329610Sgdamore@opensolaris.org 
18339610Sgdamore@opensolaris.org 	(void) snprintf(buf, sizeof (buf), "%sc%d", driver, instance);
18349610Sgdamore@opensolaris.org 	hmep->hme_intrstats = kstat_create(driver, instance, buf, "controller",
18359610Sgdamore@opensolaris.org 	    KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
18369610Sgdamore@opensolaris.org 	if (hmep->hme_intrstats)
18379610Sgdamore@opensolaris.org 		kstat_install(hmep->hme_intrstats);
18389610Sgdamore@opensolaris.org 
18399610Sgdamore@opensolaris.org 	hmep->hme_ksp = ksp;
18409610Sgdamore@opensolaris.org 	hkp = (struct hmekstat *)ksp->ks_data;
18419610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_cvc,			"code_violations",
18429610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18439610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_lenerr,		"len_errors",
18449610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18459610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_buff,			"buff",
18469610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18479610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_missed,		"missed",
18489610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18499610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_nocanput,		"nocanput",
18509610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18519610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_allocbfail,		"allocbfail",
18529610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18539610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_babl,			"babble",
18549610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18559610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_tmder,		"tmd_error",
18569610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18579610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_txlaterr,		"tx_late_error",
18589610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18599610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_rxlaterr,		"rx_late_error",
18609610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18619610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_slvparerr,		"slv_parity_error",
18629610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18639610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_txparerr,		"tx_parity_error",
18649610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18659610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_rxparerr,		"rx_parity_error",
18669610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18679610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_slverrack,		"slv_error_ack",
18689610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18699610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_txerrack,		"tx_error_ack",
18709610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18719610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_rxerrack,		"rx_error_ack",
18729610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18739610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_txtagerr,		"tx_tag_error",
18749610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18759610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_rxtagerr,		"rx_tag_error",
18769610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18779610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_eoperr,		"eop_error",
18789610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18799610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_notmds,		"no_tmds",
18809610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18819610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_notbufs,		"no_tbufs",
18829610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18839610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_norbufs,		"no_rbufs",
18849610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18859610Sgdamore@opensolaris.org 
18869610Sgdamore@opensolaris.org 	/*
18879610Sgdamore@opensolaris.org 	 * Debugging kstats
18889610Sgdamore@opensolaris.org 	 */
18899610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_inits,		"inits",
18909610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18919610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_phyfail,		"phy_failures",
18929610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18939610Sgdamore@opensolaris.org 
18949610Sgdamore@opensolaris.org 	/*
18959610Sgdamore@opensolaris.org 	 * xcvr kstats
18969610Sgdamore@opensolaris.org 	 */
18979610Sgdamore@opensolaris.org 	kstat_named_init(&hkp->hk_asic_rev,		"asic_rev",
18989610Sgdamore@opensolaris.org 	    KSTAT_DATA_ULONG);
18999610Sgdamore@opensolaris.org 
19009610Sgdamore@opensolaris.org 	ksp->ks_update = hmestat_kstat_update;
19019610Sgdamore@opensolaris.org 	ksp->ks_private = (void *) hmep;
19029610Sgdamore@opensolaris.org 	kstat_install(ksp);
19039610Sgdamore@opensolaris.org }
19049610Sgdamore@opensolaris.org 
190510806Sgdamore@opensolaris.org int
hme_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)190611878SVenu.Iyer@Sun.COM hme_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
190711878SVenu.Iyer@Sun.COM     void *val)
19089610Sgdamore@opensolaris.org {
190910806Sgdamore@opensolaris.org 	struct hme *hmep = arg;
191010806Sgdamore@opensolaris.org 	int value;
191110806Sgdamore@opensolaris.org 	int rv;
191210806Sgdamore@opensolaris.org 
191311878SVenu.Iyer@Sun.COM 	rv = mii_m_getprop(hmep->hme_mii, name, num, sz, val);
191410806Sgdamore@opensolaris.org 	if (rv != ENOTSUP)
191510806Sgdamore@opensolaris.org 		return (rv);
191610806Sgdamore@opensolaris.org 
191710806Sgdamore@opensolaris.org 	switch (num) {
191810806Sgdamore@opensolaris.org 	case MAC_PROP_PRIVATE:
191910806Sgdamore@opensolaris.org 		break;
192010806Sgdamore@opensolaris.org 	default:
192110806Sgdamore@opensolaris.org 		return (ENOTSUP);
192210806Sgdamore@opensolaris.org 	}
192310806Sgdamore@opensolaris.org 
192410806Sgdamore@opensolaris.org 	if (strcmp(name, "_ipg0") == 0) {
192511878SVenu.Iyer@Sun.COM 		value = hmep->hme_ipg0;
192610806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_ipg1") == 0) {
192711878SVenu.Iyer@Sun.COM 		value = hmep->hme_ipg1;
192810806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_ipg2") == 0) {
192911878SVenu.Iyer@Sun.COM 		value = hmep->hme_ipg2;
193010806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_lance_mode") == 0) {
193111878SVenu.Iyer@Sun.COM 		value = hmep->hme_lance_mode;
193210806Sgdamore@opensolaris.org 	} else {
193310806Sgdamore@opensolaris.org 		return (ENOTSUP);
193410806Sgdamore@opensolaris.org 	}
193510806Sgdamore@opensolaris.org 	(void) snprintf(val, sz, "%d", value);
193610806Sgdamore@opensolaris.org 	return (0);
193710806Sgdamore@opensolaris.org }
193810806Sgdamore@opensolaris.org 
193911878SVenu.Iyer@Sun.COM static void
hme_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t mph)194011878SVenu.Iyer@Sun.COM hme_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
194111878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t mph)
194211878SVenu.Iyer@Sun.COM {
194311878SVenu.Iyer@Sun.COM 	struct hme *hmep = arg;
194411878SVenu.Iyer@Sun.COM 
194511878SVenu.Iyer@Sun.COM 	mii_m_propinfo(hmep->hme_mii, name, num, mph);
194611878SVenu.Iyer@Sun.COM 
194711878SVenu.Iyer@Sun.COM 	switch (num) {
194811878SVenu.Iyer@Sun.COM 	case MAC_PROP_PRIVATE: {
194911878SVenu.Iyer@Sun.COM 		char valstr[64];
195011878SVenu.Iyer@Sun.COM 		int default_val;
195111878SVenu.Iyer@Sun.COM 
195211878SVenu.Iyer@Sun.COM 		if (strcmp(name, "_ipg0") == 0) {
195311878SVenu.Iyer@Sun.COM 			default_val = hme_ipg0;
195411878SVenu.Iyer@Sun.COM 		} else if (strcmp(name, "_ipg1") == 0) {
195511878SVenu.Iyer@Sun.COM 			default_val = hme_ipg1;
195611878SVenu.Iyer@Sun.COM 		} else if (strcmp(name, "_ipg2") == 0) {
195711878SVenu.Iyer@Sun.COM 			default_val = hme_ipg2;
195811878SVenu.Iyer@Sun.COM 		} if (strcmp(name, "_lance_mode") == 0) {
195911878SVenu.Iyer@Sun.COM 			default_val = hme_lance_mode;
196011878SVenu.Iyer@Sun.COM 		} else {
196111878SVenu.Iyer@Sun.COM 			return;
196211878SVenu.Iyer@Sun.COM 		}
196311878SVenu.Iyer@Sun.COM 
196411878SVenu.Iyer@Sun.COM 		(void) snprintf(valstr, sizeof (valstr), "%d", default_val);
196511878SVenu.Iyer@Sun.COM 		mac_prop_info_set_default_str(mph, valstr);
196611878SVenu.Iyer@Sun.COM 		break;
196711878SVenu.Iyer@Sun.COM 	}
196811878SVenu.Iyer@Sun.COM 	}
196911878SVenu.Iyer@Sun.COM }
197011878SVenu.Iyer@Sun.COM 
197110806Sgdamore@opensolaris.org int
hme_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)197210806Sgdamore@opensolaris.org hme_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
197310806Sgdamore@opensolaris.org     const void *val)
197410806Sgdamore@opensolaris.org {
197510806Sgdamore@opensolaris.org 	struct hme *hmep = arg;
197610806Sgdamore@opensolaris.org 	int rv;
197710806Sgdamore@opensolaris.org 	long lval;
197810806Sgdamore@opensolaris.org 	boolean_t init = B_FALSE;
197910806Sgdamore@opensolaris.org 
198010806Sgdamore@opensolaris.org 	rv = mii_m_setprop(hmep->hme_mii, name, num, sz, val);
198110806Sgdamore@opensolaris.org 	if (rv != ENOTSUP)
198210806Sgdamore@opensolaris.org 		return (rv);
198310806Sgdamore@opensolaris.org 	rv = 0;
198410806Sgdamore@opensolaris.org 
198510806Sgdamore@opensolaris.org 	switch (num) {
198610806Sgdamore@opensolaris.org 	case MAC_PROP_PRIVATE:
198710806Sgdamore@opensolaris.org 		break;
198810806Sgdamore@opensolaris.org 	default:
198910806Sgdamore@opensolaris.org 		return (ENOTSUP);
199010806Sgdamore@opensolaris.org 	}
199110806Sgdamore@opensolaris.org 
199210806Sgdamore@opensolaris.org 	(void) ddi_strtol(val, NULL, 0, &lval);
199310806Sgdamore@opensolaris.org 
199410806Sgdamore@opensolaris.org 	if (strcmp(name, "_ipg1") == 0) {
199510806Sgdamore@opensolaris.org 		if ((lval >= 0) && (lval <= 255)) {
199610806Sgdamore@opensolaris.org 			hmep->hme_ipg1 = lval & 0xff;
199710806Sgdamore@opensolaris.org 			init = B_TRUE;
199810806Sgdamore@opensolaris.org 		} else {
199910806Sgdamore@opensolaris.org 			return (EINVAL);
20009610Sgdamore@opensolaris.org 		}
200110806Sgdamore@opensolaris.org 
200210806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_ipg2") == 0) {
200310806Sgdamore@opensolaris.org 		if ((lval >= 0) && (lval <= 255)) {
200410806Sgdamore@opensolaris.org 			hmep->hme_ipg2 = lval & 0xff;
200510806Sgdamore@opensolaris.org 			init = B_TRUE;
200610806Sgdamore@opensolaris.org 		} else {
200710806Sgdamore@opensolaris.org 			return (EINVAL);
200810806Sgdamore@opensolaris.org 		}
200910806Sgdamore@opensolaris.org 
201010806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_ipg0") == 0) {
201110806Sgdamore@opensolaris.org 		if ((lval >= 0) && (lval <= 31)) {
201210806Sgdamore@opensolaris.org 			hmep->hme_ipg0 = lval & 0xff;
201310806Sgdamore@opensolaris.org 			init = B_TRUE;
201410806Sgdamore@opensolaris.org 		} else {
201510806Sgdamore@opensolaris.org 			return (EINVAL);
20169610Sgdamore@opensolaris.org 		}
201710806Sgdamore@opensolaris.org 	} else if (strcmp(name, "_lance_mode") == 0) {
201810806Sgdamore@opensolaris.org 		if ((lval >= 0) && (lval <= 1)) {
201910806Sgdamore@opensolaris.org 			hmep->hme_lance_mode = lval & 0xff;
202010806Sgdamore@opensolaris.org 			init = B_TRUE;
20219610Sgdamore@opensolaris.org 		} else {
202210806Sgdamore@opensolaris.org 			return (EINVAL);
20239610Sgdamore@opensolaris.org 		}
202410806Sgdamore@opensolaris.org 
202510806Sgdamore@opensolaris.org 	} else {
202610806Sgdamore@opensolaris.org 		rv = ENOTSUP;
20279610Sgdamore@opensolaris.org 	}
202810806Sgdamore@opensolaris.org 
202910806Sgdamore@opensolaris.org 	if (init) {
203010806Sgdamore@opensolaris.org 		(void) hmeinit(hmep);
203110806Sgdamore@opensolaris.org 	}
203210806Sgdamore@opensolaris.org 	return (rv);
20339610Sgdamore@opensolaris.org }
20349610Sgdamore@opensolaris.org 
203510806Sgdamore@opensolaris.org 
20369610Sgdamore@opensolaris.org /*ARGSUSED*/
20379610Sgdamore@opensolaris.org static boolean_t
hme_m_getcapab(void * arg,mac_capab_t cap,void * cap_data)20389610Sgdamore@opensolaris.org hme_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
20399610Sgdamore@opensolaris.org {
20409610Sgdamore@opensolaris.org 	switch (cap) {
20419610Sgdamore@opensolaris.org 	case MAC_CAPAB_HCKSUM:
20429610Sgdamore@opensolaris.org 		*(uint32_t *)cap_data = HCKSUM_INET_PARTIAL;
20439610Sgdamore@opensolaris.org 		return (B_TRUE);
20449610Sgdamore@opensolaris.org 	default:
20459610Sgdamore@opensolaris.org 		return (B_FALSE);
20469610Sgdamore@opensolaris.org 	}
20479610Sgdamore@opensolaris.org }
20489610Sgdamore@opensolaris.org 
20499610Sgdamore@opensolaris.org static int
hme_m_promisc(void * arg,boolean_t on)20509610Sgdamore@opensolaris.org hme_m_promisc(void *arg, boolean_t on)
20519610Sgdamore@opensolaris.org {
20529610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
20539610Sgdamore@opensolaris.org 
20549610Sgdamore@opensolaris.org 	hmep->hme_promisc = on;
20559610Sgdamore@opensolaris.org 	(void) hmeinit(hmep);
20569610Sgdamore@opensolaris.org 	return (0);
20579610Sgdamore@opensolaris.org }
20589610Sgdamore@opensolaris.org 
20599610Sgdamore@opensolaris.org static int
hme_m_unicst(void * arg,const uint8_t * macaddr)20609610Sgdamore@opensolaris.org hme_m_unicst(void *arg, const uint8_t *macaddr)
20619610Sgdamore@opensolaris.org {
20629610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
20639610Sgdamore@opensolaris.org 
20649610Sgdamore@opensolaris.org 	/*
20659610Sgdamore@opensolaris.org 	 * Set new interface local address and re-init device.
20669610Sgdamore@opensolaris.org 	 * This is destructive to any other streams attached
20679610Sgdamore@opensolaris.org 	 * to this device.
20689610Sgdamore@opensolaris.org 	 */
20699610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_intrlock);
20709610Sgdamore@opensolaris.org 	bcopy(macaddr, &hmep->hme_ouraddr, ETHERADDRL);
20719610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_intrlock);
20729610Sgdamore@opensolaris.org 	(void) hmeinit(hmep);
20739610Sgdamore@opensolaris.org 	return (0);
20749610Sgdamore@opensolaris.org }
20759610Sgdamore@opensolaris.org 
20769610Sgdamore@opensolaris.org static int
hme_m_multicst(void * arg,boolean_t add,const uint8_t * macaddr)20779610Sgdamore@opensolaris.org hme_m_multicst(void *arg, boolean_t add, const uint8_t *macaddr)
20789610Sgdamore@opensolaris.org {
20799610Sgdamore@opensolaris.org 	struct hme	*hmep = arg;
20809610Sgdamore@opensolaris.org 	uint32_t	ladrf_bit;
20819610Sgdamore@opensolaris.org 	boolean_t	doinit = B_FALSE;
20829610Sgdamore@opensolaris.org 
20839610Sgdamore@opensolaris.org 	/*
20849610Sgdamore@opensolaris.org 	 * If this address's bit was not already set in the local address
20859610Sgdamore@opensolaris.org 	 * filter, add it and re-initialize the Hardware.
20869610Sgdamore@opensolaris.org 	 */
20879610Sgdamore@opensolaris.org 	ladrf_bit = hmeladrf_bit(macaddr);
20889610Sgdamore@opensolaris.org 
20899610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_intrlock);
20909610Sgdamore@opensolaris.org 	if (add) {
20919610Sgdamore@opensolaris.org 		hmep->hme_ladrf_refcnt[ladrf_bit]++;
20929610Sgdamore@opensolaris.org 		if (hmep->hme_ladrf_refcnt[ladrf_bit] == 1) {
20939610Sgdamore@opensolaris.org 			hmep->hme_ladrf[ladrf_bit >> 4] |=
20949610Sgdamore@opensolaris.org 			    1 << (ladrf_bit & 0xf);
20959610Sgdamore@opensolaris.org 			hmep->hme_multi++;
20969610Sgdamore@opensolaris.org 			doinit = B_TRUE;
20979610Sgdamore@opensolaris.org 		}
20989610Sgdamore@opensolaris.org 	} else {
20999610Sgdamore@opensolaris.org 		hmep->hme_ladrf_refcnt[ladrf_bit]--;
21009610Sgdamore@opensolaris.org 		if (hmep->hme_ladrf_refcnt[ladrf_bit] == 0) {
21019610Sgdamore@opensolaris.org 			hmep->hme_ladrf[ladrf_bit >> 4] &=
21029610Sgdamore@opensolaris.org 			    ~(1 << (ladrf_bit & 0xf));
21039610Sgdamore@opensolaris.org 			doinit = B_TRUE;
21049610Sgdamore@opensolaris.org 		}
21059610Sgdamore@opensolaris.org 	}
21069610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_intrlock);
21079610Sgdamore@opensolaris.org 
21089610Sgdamore@opensolaris.org 	if (doinit) {
21099610Sgdamore@opensolaris.org 		(void) hmeinit(hmep);
21109610Sgdamore@opensolaris.org 	}
21119610Sgdamore@opensolaris.org 
21129610Sgdamore@opensolaris.org 	return (0);
21139610Sgdamore@opensolaris.org }
21149610Sgdamore@opensolaris.org 
21159610Sgdamore@opensolaris.org static int
hme_m_start(void * arg)21169610Sgdamore@opensolaris.org hme_m_start(void *arg)
21179610Sgdamore@opensolaris.org {
21189610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
21199610Sgdamore@opensolaris.org 
21209610Sgdamore@opensolaris.org 	if (hmeinit(hmep) != 0) {
21219610Sgdamore@opensolaris.org 		/* initialization failed -- really want DL_INITFAILED */
21229610Sgdamore@opensolaris.org 		return (EIO);
21239610Sgdamore@opensolaris.org 	} else {
21249610Sgdamore@opensolaris.org 		hmep->hme_started = B_TRUE;
212510806Sgdamore@opensolaris.org 		mii_start(hmep->hme_mii);
21269610Sgdamore@opensolaris.org 		return (0);
21279610Sgdamore@opensolaris.org 	}
21289610Sgdamore@opensolaris.org }
21299610Sgdamore@opensolaris.org 
21309610Sgdamore@opensolaris.org static void
hme_m_stop(void * arg)21319610Sgdamore@opensolaris.org hme_m_stop(void *arg)
21329610Sgdamore@opensolaris.org {
21339610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
21349610Sgdamore@opensolaris.org 
213510806Sgdamore@opensolaris.org 	mii_stop(hmep->hme_mii);
21369610Sgdamore@opensolaris.org 	hmep->hme_started = B_FALSE;
21379610Sgdamore@opensolaris.org 	hmeuninit(hmep);
21389610Sgdamore@opensolaris.org }
21399610Sgdamore@opensolaris.org 
21409610Sgdamore@opensolaris.org static int
hme_m_stat(void * arg,uint_t stat,uint64_t * val)21419610Sgdamore@opensolaris.org hme_m_stat(void *arg, uint_t stat, uint64_t *val)
21429610Sgdamore@opensolaris.org {
21439610Sgdamore@opensolaris.org 	struct hme	*hmep = arg;
21449610Sgdamore@opensolaris.org 
21459610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_xmitlock);
21469610Sgdamore@opensolaris.org 	if (hmep->hme_flags & HMERUNNING) {
21479610Sgdamore@opensolaris.org 		hmereclaim(hmep);
21489610Sgdamore@opensolaris.org 		hmesavecntrs(hmep);
21499610Sgdamore@opensolaris.org 	}
21509610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
21519610Sgdamore@opensolaris.org 
21529610Sgdamore@opensolaris.org 
215310806Sgdamore@opensolaris.org 	if (mii_m_getstat(hmep->hme_mii, stat, val) == 0) {
215410806Sgdamore@opensolaris.org 		return (0);
215510806Sgdamore@opensolaris.org 	}
21569610Sgdamore@opensolaris.org 	switch (stat) {
21579610Sgdamore@opensolaris.org 	case MAC_STAT_IPACKETS:
21589610Sgdamore@opensolaris.org 		*val = hmep->hme_ipackets;
21599610Sgdamore@opensolaris.org 		break;
21609610Sgdamore@opensolaris.org 	case MAC_STAT_RBYTES:
21619610Sgdamore@opensolaris.org 		*val = hmep->hme_rbytes;
21629610Sgdamore@opensolaris.org 		break;
21639610Sgdamore@opensolaris.org 	case MAC_STAT_IERRORS:
21649610Sgdamore@opensolaris.org 		*val = hmep->hme_ierrors;
21659610Sgdamore@opensolaris.org 		break;
21669610Sgdamore@opensolaris.org 	case MAC_STAT_OPACKETS:
21679610Sgdamore@opensolaris.org 		*val = hmep->hme_opackets;
21689610Sgdamore@opensolaris.org 		break;
21699610Sgdamore@opensolaris.org 	case MAC_STAT_OBYTES:
21709610Sgdamore@opensolaris.org 		*val = hmep->hme_obytes;
21719610Sgdamore@opensolaris.org 		break;
21729610Sgdamore@opensolaris.org 	case MAC_STAT_OERRORS:
21739610Sgdamore@opensolaris.org 		*val = hmep->hme_oerrors;
21749610Sgdamore@opensolaris.org 		break;
21759610Sgdamore@opensolaris.org 	case MAC_STAT_MULTIRCV:
21769610Sgdamore@opensolaris.org 		*val = hmep->hme_multircv;
21779610Sgdamore@opensolaris.org 		break;
21789610Sgdamore@opensolaris.org 	case MAC_STAT_MULTIXMT:
21799610Sgdamore@opensolaris.org 		*val = hmep->hme_multixmt;
21809610Sgdamore@opensolaris.org 		break;
21819610Sgdamore@opensolaris.org 	case MAC_STAT_BRDCSTRCV:
21829610Sgdamore@opensolaris.org 		*val = hmep->hme_brdcstrcv;
21839610Sgdamore@opensolaris.org 		break;
21849610Sgdamore@opensolaris.org 	case MAC_STAT_BRDCSTXMT:
21859610Sgdamore@opensolaris.org 		*val = hmep->hme_brdcstxmt;
21869610Sgdamore@opensolaris.org 		break;
21879610Sgdamore@opensolaris.org 	case MAC_STAT_UNDERFLOWS:
21889610Sgdamore@opensolaris.org 		*val = hmep->hme_uflo;
21899610Sgdamore@opensolaris.org 		break;
21909610Sgdamore@opensolaris.org 	case MAC_STAT_OVERFLOWS:
21919610Sgdamore@opensolaris.org 		*val = hmep->hme_oflo;
21929610Sgdamore@opensolaris.org 		break;
21939610Sgdamore@opensolaris.org 	case MAC_STAT_COLLISIONS:
21949610Sgdamore@opensolaris.org 		*val = hmep->hme_coll;
21959610Sgdamore@opensolaris.org 		break;
21969610Sgdamore@opensolaris.org 	case MAC_STAT_NORCVBUF:
21979610Sgdamore@opensolaris.org 		*val = hmep->hme_norcvbuf;
21989610Sgdamore@opensolaris.org 		break;
21999610Sgdamore@opensolaris.org 	case MAC_STAT_NOXMTBUF:
22009610Sgdamore@opensolaris.org 		*val = hmep->hme_noxmtbuf;
22019610Sgdamore@opensolaris.org 		break;
22029610Sgdamore@opensolaris.org 	case ETHER_STAT_LINK_DUPLEX:
22039610Sgdamore@opensolaris.org 		*val = hmep->hme_duplex;
22049610Sgdamore@opensolaris.org 		break;
22059610Sgdamore@opensolaris.org 	case ETHER_STAT_ALIGN_ERRORS:
22069610Sgdamore@opensolaris.org 		*val = hmep->hme_align_errors;
22079610Sgdamore@opensolaris.org 		break;
22089610Sgdamore@opensolaris.org 	case ETHER_STAT_FCS_ERRORS:
22099610Sgdamore@opensolaris.org 		*val = hmep->hme_fcs_errors;
22109610Sgdamore@opensolaris.org 		break;
22119610Sgdamore@opensolaris.org 	case ETHER_STAT_EX_COLLISIONS:
22129610Sgdamore@opensolaris.org 		*val = hmep->hme_excol;
22139610Sgdamore@opensolaris.org 		break;
22149610Sgdamore@opensolaris.org 	case ETHER_STAT_DEFER_XMTS:
22159610Sgdamore@opensolaris.org 		*val = hmep->hme_defer_xmts;
22169610Sgdamore@opensolaris.org 		break;
22179610Sgdamore@opensolaris.org 	case ETHER_STAT_SQE_ERRORS:
22189610Sgdamore@opensolaris.org 		*val = hmep->hme_sqe_errors;
22199610Sgdamore@opensolaris.org 		break;
22209610Sgdamore@opensolaris.org 	case ETHER_STAT_FIRST_COLLISIONS:
22219610Sgdamore@opensolaris.org 		*val = hmep->hme_fstcol;
22229610Sgdamore@opensolaris.org 		break;
22239610Sgdamore@opensolaris.org 	case ETHER_STAT_TX_LATE_COLLISIONS:
22249610Sgdamore@opensolaris.org 		*val = hmep->hme_tlcol;
22259610Sgdamore@opensolaris.org 		break;
22269610Sgdamore@opensolaris.org 	case ETHER_STAT_TOOLONG_ERRORS:
22279610Sgdamore@opensolaris.org 		*val = hmep->hme_toolong_errors;
22289610Sgdamore@opensolaris.org 		break;
22299610Sgdamore@opensolaris.org 	case ETHER_STAT_TOOSHORT_ERRORS:
22309610Sgdamore@opensolaris.org 		*val = hmep->hme_runt;
22319610Sgdamore@opensolaris.org 		break;
22329610Sgdamore@opensolaris.org 	case ETHER_STAT_CARRIER_ERRORS:
22339610Sgdamore@opensolaris.org 		*val = hmep->hme_carrier_errors;
22349610Sgdamore@opensolaris.org 		break;
22359610Sgdamore@opensolaris.org 	default:
22369610Sgdamore@opensolaris.org 		return (EINVAL);
22379610Sgdamore@opensolaris.org 	}
22389610Sgdamore@opensolaris.org 	return (0);
22399610Sgdamore@opensolaris.org }
22409610Sgdamore@opensolaris.org 
22419610Sgdamore@opensolaris.org static mblk_t *
hme_m_tx(void * arg,mblk_t * mp)22429610Sgdamore@opensolaris.org hme_m_tx(void *arg, mblk_t *mp)
22439610Sgdamore@opensolaris.org {
22449610Sgdamore@opensolaris.org 	struct hme *hmep = arg;
22459610Sgdamore@opensolaris.org 	mblk_t *next;
22469610Sgdamore@opensolaris.org 
22479610Sgdamore@opensolaris.org 	while (mp != NULL) {
22489610Sgdamore@opensolaris.org 		next = mp->b_next;
22499610Sgdamore@opensolaris.org 		mp->b_next = NULL;
22509610Sgdamore@opensolaris.org 		if (!hmestart(hmep, mp)) {
22519610Sgdamore@opensolaris.org 			mp->b_next = next;
22529610Sgdamore@opensolaris.org 			break;
22539610Sgdamore@opensolaris.org 		}
22549610Sgdamore@opensolaris.org 		mp = next;
22559610Sgdamore@opensolaris.org 	}
22569610Sgdamore@opensolaris.org 	return (mp);
22579610Sgdamore@opensolaris.org }
22589610Sgdamore@opensolaris.org 
22599610Sgdamore@opensolaris.org /*
22609610Sgdamore@opensolaris.org  * Software IP checksum, for the edge cases that the
22619610Sgdamore@opensolaris.org  * hardware can't handle.  See hmestart for more info.
22629610Sgdamore@opensolaris.org  */
22639610Sgdamore@opensolaris.org static uint16_t
hme_cksum(void * data,int len)22649610Sgdamore@opensolaris.org hme_cksum(void *data, int len)
22659610Sgdamore@opensolaris.org {
22669610Sgdamore@opensolaris.org 	uint16_t	*words = data;
22679610Sgdamore@opensolaris.org 	int		i, nwords = len / 2;
22689610Sgdamore@opensolaris.org 	uint32_t	sum = 0;
22699610Sgdamore@opensolaris.org 
22709610Sgdamore@opensolaris.org 	/* just add up the words */
22719610Sgdamore@opensolaris.org 	for (i = 0; i < nwords; i++) {
22729610Sgdamore@opensolaris.org 		sum += *words++;
22739610Sgdamore@opensolaris.org 	}
22749610Sgdamore@opensolaris.org 
22759610Sgdamore@opensolaris.org 	/* pick up residual byte ... assume even half-word allocations */
22769610Sgdamore@opensolaris.org 	if (len % 2) {
22779610Sgdamore@opensolaris.org 		sum += (*words & htons(0xff00));
22789610Sgdamore@opensolaris.org 	}
22799610Sgdamore@opensolaris.org 
22809610Sgdamore@opensolaris.org 	sum = (sum >> 16) + (sum & 0xffff);
22819610Sgdamore@opensolaris.org 	sum = (sum >> 16) + (sum & 0xffff);
22829610Sgdamore@opensolaris.org 
22839610Sgdamore@opensolaris.org 	return (~(sum & 0xffff));
22849610Sgdamore@opensolaris.org }
22859610Sgdamore@opensolaris.org 
22869610Sgdamore@opensolaris.org static boolean_t
hmestart(struct hme * hmep,mblk_t * mp)22879610Sgdamore@opensolaris.org hmestart(struct hme *hmep, mblk_t *mp)
22889610Sgdamore@opensolaris.org {
22899610Sgdamore@opensolaris.org 	uint32_t	len;
22909610Sgdamore@opensolaris.org 	boolean_t	retval = B_TRUE;
22919610Sgdamore@opensolaris.org 	hmebuf_t	*tbuf;
22929610Sgdamore@opensolaris.org 	uint32_t	txptr;
22939610Sgdamore@opensolaris.org 
22949610Sgdamore@opensolaris.org 	uint32_t	csflags = 0;
22959610Sgdamore@opensolaris.org 	uint32_t	flags;
22969610Sgdamore@opensolaris.org 	uint32_t	start_offset;
22979610Sgdamore@opensolaris.org 	uint32_t	stuff_offset;
22989610Sgdamore@opensolaris.org 
229911878SVenu.Iyer@Sun.COM 	mac_hcksum_get(mp, &start_offset, &stuff_offset, NULL, NULL, &flags);
23009610Sgdamore@opensolaris.org 
23019610Sgdamore@opensolaris.org 	if (flags & HCK_PARTIALCKSUM) {
23029610Sgdamore@opensolaris.org 		if (get_ether_type(mp->b_rptr) == ETHERTYPE_VLAN) {
23039610Sgdamore@opensolaris.org 			start_offset += sizeof (struct ether_header) + 4;
23049610Sgdamore@opensolaris.org 			stuff_offset += sizeof (struct ether_header) + 4;
23059610Sgdamore@opensolaris.org 		} else {
23069610Sgdamore@opensolaris.org 			start_offset += sizeof (struct ether_header);
23079610Sgdamore@opensolaris.org 			stuff_offset += sizeof (struct ether_header);
23089610Sgdamore@opensolaris.org 		}
23099610Sgdamore@opensolaris.org 		csflags = HMETMD_CSENABL |
23109610Sgdamore@opensolaris.org 		    (start_offset << HMETMD_CSSTART_SHIFT) |
23119610Sgdamore@opensolaris.org 		    (stuff_offset << HMETMD_CSSTUFF_SHIFT);
23129610Sgdamore@opensolaris.org 	}
23139610Sgdamore@opensolaris.org 
23149610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_xmitlock);
23159610Sgdamore@opensolaris.org 
23169610Sgdamore@opensolaris.org 	if (hmep->hme_flags & HMESUSPENDED) {
23179610Sgdamore@opensolaris.org 		hmep->hme_carrier_errors++;
23189610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
23199610Sgdamore@opensolaris.org 		goto bad;
23209610Sgdamore@opensolaris.org 	}
23219610Sgdamore@opensolaris.org 
23229610Sgdamore@opensolaris.org 	if (hmep->hme_txindex != hmep->hme_txreclaim) {
23239610Sgdamore@opensolaris.org 		hmereclaim(hmep);
23249610Sgdamore@opensolaris.org 	}
23259610Sgdamore@opensolaris.org 	if ((hmep->hme_txindex - HME_TMDMAX) == hmep->hme_txreclaim)
23269610Sgdamore@opensolaris.org 		goto notmds;
23279610Sgdamore@opensolaris.org 	txptr = hmep->hme_txindex % HME_TMDMAX;
23289610Sgdamore@opensolaris.org 	tbuf = &hmep->hme_tbuf[txptr];
23299610Sgdamore@opensolaris.org 
23309610Sgdamore@opensolaris.org 	/*
23319610Sgdamore@opensolaris.org 	 * Note that for checksum offload, the hardware cannot
23329610Sgdamore@opensolaris.org 	 * generate correct checksums if the packet is smaller than
23339610Sgdamore@opensolaris.org 	 * 64-bytes.  In such a case, we bcopy the packet and use
23349610Sgdamore@opensolaris.org 	 * a software checksum.
23359610Sgdamore@opensolaris.org 	 */
23369610Sgdamore@opensolaris.org 
23379610Sgdamore@opensolaris.org 	len = msgsize(mp);
23389610Sgdamore@opensolaris.org 	if (len < 64) {
23399610Sgdamore@opensolaris.org 		/* zero fill the padding */
23409610Sgdamore@opensolaris.org 		bzero(tbuf->kaddr, 64);
23419610Sgdamore@opensolaris.org 	}
23429610Sgdamore@opensolaris.org 	mcopymsg(mp, tbuf->kaddr);
23439610Sgdamore@opensolaris.org 
2344*12981SZeeshanul.Huq@Sun.COM 	if ((csflags != 0) && ((len < 64) ||
2345*12981SZeeshanul.Huq@Sun.COM 	    (start_offset > HMETMD_CSSTART_MAX) ||
2346*12981SZeeshanul.Huq@Sun.COM 	    (stuff_offset > HMETMD_CSSTUFF_MAX))) {
23479610Sgdamore@opensolaris.org 		uint16_t sum;
23489610Sgdamore@opensolaris.org 		sum = hme_cksum(tbuf->kaddr + start_offset,
23499610Sgdamore@opensolaris.org 		    len - start_offset);
23509610Sgdamore@opensolaris.org 		bcopy(&sum, tbuf->kaddr + stuff_offset, sizeof (sum));
23519610Sgdamore@opensolaris.org 		csflags = 0;
23529610Sgdamore@opensolaris.org 	}
23539610Sgdamore@opensolaris.org 
23549610Sgdamore@opensolaris.org 	if (ddi_dma_sync(tbuf->dmah, 0, len, DDI_DMA_SYNC_FORDEV) ==
23559610Sgdamore@opensolaris.org 	    DDI_FAILURE) {
23569610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, DDI_MSG,
23579610Sgdamore@opensolaris.org 		    "ddi_dma_sync failed");
23589610Sgdamore@opensolaris.org 	}
23599610Sgdamore@opensolaris.org 
23609610Sgdamore@opensolaris.org 	/*
23619610Sgdamore@opensolaris.org 	 * update MIB II statistics
23629610Sgdamore@opensolaris.org 	 */
23639610Sgdamore@opensolaris.org 	BUMP_OutNUcast(hmep, tbuf->kaddr);
23649610Sgdamore@opensolaris.org 
23659610Sgdamore@opensolaris.org 	PUT_TMD(txptr, tbuf->paddr, len,
23669610Sgdamore@opensolaris.org 	    HMETMD_OWN | HMETMD_SOP | HMETMD_EOP | csflags);
23679610Sgdamore@opensolaris.org 
23689610Sgdamore@opensolaris.org 	HMESYNCTMD(txptr, DDI_DMA_SYNC_FORDEV);
23699610Sgdamore@opensolaris.org 	hmep->hme_txindex++;
23709610Sgdamore@opensolaris.org 
23719610Sgdamore@opensolaris.org 	PUT_ETXREG(txpend, HMET_TXPEND_TDMD);
23729610Sgdamore@opensolaris.org 	CHECK_ETXREG();
23739610Sgdamore@opensolaris.org 
23749610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
23759610Sgdamore@opensolaris.org 
23769610Sgdamore@opensolaris.org 	hmep->hme_starts++;
23779610Sgdamore@opensolaris.org 	return (B_TRUE);
23789610Sgdamore@opensolaris.org 
23799610Sgdamore@opensolaris.org bad:
23809610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
23819610Sgdamore@opensolaris.org 	freemsg(mp);
23829610Sgdamore@opensolaris.org 	return (B_TRUE);
23839610Sgdamore@opensolaris.org 
23849610Sgdamore@opensolaris.org notmds:
23859610Sgdamore@opensolaris.org 	hmep->hme_notmds++;
23869610Sgdamore@opensolaris.org 	hmep->hme_wantw = B_TRUE;
23879610Sgdamore@opensolaris.org 	hmereclaim(hmep);
23889610Sgdamore@opensolaris.org 	retval = B_FALSE;
23899610Sgdamore@opensolaris.org done:
23909610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
23919610Sgdamore@opensolaris.org 
23929610Sgdamore@opensolaris.org 	return (retval);
23939610Sgdamore@opensolaris.org }
23949610Sgdamore@opensolaris.org 
23959610Sgdamore@opensolaris.org /*
23969610Sgdamore@opensolaris.org  * Initialize channel.
23979610Sgdamore@opensolaris.org  * Return 0 on success, nonzero on error.
23989610Sgdamore@opensolaris.org  *
23999610Sgdamore@opensolaris.org  * The recommended sequence for initialization is:
24009610Sgdamore@opensolaris.org  * 1. Issue a Global Reset command to the Ethernet Channel.
24019610Sgdamore@opensolaris.org  * 2. Poll the Global_Reset bits until the execution of the reset has been
24029610Sgdamore@opensolaris.org  *    completed.
24039610Sgdamore@opensolaris.org  * 2(a). Use the MIF Frame/Output register to reset the transceiver.
24049610Sgdamore@opensolaris.org  *	 Poll Register 0 to till the Resetbit is 0.
24059610Sgdamore@opensolaris.org  * 2(b). Use the MIF Frame/Output register to set the PHY in in Normal-Op,
24069610Sgdamore@opensolaris.org  *	 100Mbps and Non-Isolated mode. The main point here is to bring the
24079610Sgdamore@opensolaris.org  *	 PHY out of Isolate mode so that it can generate the rx_clk and tx_clk
24089610Sgdamore@opensolaris.org  *	 to the MII interface so that the Bigmac core can correctly reset
24099610Sgdamore@opensolaris.org  *	 upon a software reset.
24109610Sgdamore@opensolaris.org  * 2(c).  Issue another Global Reset command to the Ethernet Channel and poll
24119610Sgdamore@opensolaris.org  *	  the Global_Reset bits till completion.
24129610Sgdamore@opensolaris.org  * 3. Set up all the data structures in the host memory.
24139610Sgdamore@opensolaris.org  * 4. Program the TX_MAC registers/counters (excluding the TX_MAC Configuration
24149610Sgdamore@opensolaris.org  *    Register).
24159610Sgdamore@opensolaris.org  * 5. Program the RX_MAC registers/counters (excluding the RX_MAC Configuration
24169610Sgdamore@opensolaris.org  *    Register).
24179610Sgdamore@opensolaris.org  * 6. Program the Transmit Descriptor Ring Base Address in the ETX.
24189610Sgdamore@opensolaris.org  * 7. Program the Receive Descriptor Ring Base Address in the ERX.
24199610Sgdamore@opensolaris.org  * 8. Program the Global Configuration and the Global Interrupt Mask Registers.
24209610Sgdamore@opensolaris.org  * 9. Program the ETX Configuration register (enable the Transmit DMA channel).
24219610Sgdamore@opensolaris.org  * 10. Program the ERX Configuration register (enable the Receive DMA channel).
24229610Sgdamore@opensolaris.org  * 11. Program the XIF Configuration Register (enable the XIF).
24239610Sgdamore@opensolaris.org  * 12. Program the RX_MAC Configuration Register (Enable the RX_MAC).
24249610Sgdamore@opensolaris.org  * 13. Program the TX_MAC Configuration Register (Enable the TX_MAC).
24259610Sgdamore@opensolaris.org  */
24269610Sgdamore@opensolaris.org 
24279610Sgdamore@opensolaris.org 
24289610Sgdamore@opensolaris.org #ifdef FEPS_URUN_BUG
24299610Sgdamore@opensolaris.org static int hme_palen = 32;
24309610Sgdamore@opensolaris.org #endif
24319610Sgdamore@opensolaris.org 
24329610Sgdamore@opensolaris.org static int
hmeinit(struct hme * hmep)24339610Sgdamore@opensolaris.org hmeinit(struct hme *hmep)
24349610Sgdamore@opensolaris.org {
24359610Sgdamore@opensolaris.org 	uint32_t		i;
24369610Sgdamore@opensolaris.org 	int			ret;
243710806Sgdamore@opensolaris.org 	boolean_t		fdx;
243810806Sgdamore@opensolaris.org 	int			phyad;
24399610Sgdamore@opensolaris.org 
24409610Sgdamore@opensolaris.org 	/*
24419610Sgdamore@opensolaris.org 	 * Lock sequence:
24429610Sgdamore@opensolaris.org 	 *	hme_intrlock, hme_xmitlock.
24439610Sgdamore@opensolaris.org 	 */
24449610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_intrlock);
24459610Sgdamore@opensolaris.org 
24469610Sgdamore@opensolaris.org 	/*
24479610Sgdamore@opensolaris.org 	 * Don't touch the hardware if we are suspended.  But don't
24489610Sgdamore@opensolaris.org 	 * fail either.  Some time later we may be resumed, and then
24499610Sgdamore@opensolaris.org 	 * we'll be back here to program the device using the settings
24509610Sgdamore@opensolaris.org 	 * in the soft state.
24519610Sgdamore@opensolaris.org 	 */
24529610Sgdamore@opensolaris.org 	if (hmep->hme_flags & HMESUSPENDED) {
24539610Sgdamore@opensolaris.org 		mutex_exit(&hmep->hme_intrlock);
24549610Sgdamore@opensolaris.org 		return (0);
24559610Sgdamore@opensolaris.org 	}
24569610Sgdamore@opensolaris.org 
24579610Sgdamore@opensolaris.org 	/*
24589610Sgdamore@opensolaris.org 	 * This should prevent us from clearing any interrupts that
24599610Sgdamore@opensolaris.org 	 * may occur by temporarily stopping interrupts from occurring
24609610Sgdamore@opensolaris.org 	 * for a short time.  We need to update the interrupt mask
24619610Sgdamore@opensolaris.org 	 * later in this function.
24629610Sgdamore@opensolaris.org 	 */
24639610Sgdamore@opensolaris.org 	PUT_GLOBREG(intmask, ~HMEG_MASK_MIF_INTR);
24649610Sgdamore@opensolaris.org 
24659610Sgdamore@opensolaris.org 
24669610Sgdamore@opensolaris.org 	/*
24679610Sgdamore@opensolaris.org 	 * Rearranged the mutex acquisition order to solve the deadlock
24689610Sgdamore@opensolaris.org 	 * situation as described in bug ID 4065896.
24699610Sgdamore@opensolaris.org 	 */
24709610Sgdamore@opensolaris.org 
24719610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_xmitlock);
24729610Sgdamore@opensolaris.org 
24739610Sgdamore@opensolaris.org 	hmep->hme_flags = 0;
24749610Sgdamore@opensolaris.org 	hmep->hme_wantw = B_FALSE;
24759610Sgdamore@opensolaris.org 
24769610Sgdamore@opensolaris.org 	if (hmep->inits)
24779610Sgdamore@opensolaris.org 		hmesavecntrs(hmep);
24789610Sgdamore@opensolaris.org 
24799610Sgdamore@opensolaris.org 	/*
24809610Sgdamore@opensolaris.org 	 * Perform Global reset of the Sbus/FEPS ENET channel.
24819610Sgdamore@opensolaris.org 	 */
24829610Sgdamore@opensolaris.org 	(void) hmestop(hmep);
24839610Sgdamore@opensolaris.org 
24849610Sgdamore@opensolaris.org 	/*
24859610Sgdamore@opensolaris.org 	 * Clear all descriptors.
24869610Sgdamore@opensolaris.org 	 */
24879610Sgdamore@opensolaris.org 	bzero(hmep->hme_rmdp, HME_RMDMAX * sizeof (struct hme_rmd));
24889610Sgdamore@opensolaris.org 	bzero(hmep->hme_tmdp, HME_TMDMAX * sizeof (struct hme_tmd));
24899610Sgdamore@opensolaris.org 
24909610Sgdamore@opensolaris.org 	/*
24919610Sgdamore@opensolaris.org 	 * Hang out receive buffers.
24929610Sgdamore@opensolaris.org 	 */
24939610Sgdamore@opensolaris.org 	for (i = 0; i < HME_RMDMAX; i++) {
24949610Sgdamore@opensolaris.org 		PUT_RMD(i, hmep->hme_rbuf[i].paddr);
24959610Sgdamore@opensolaris.org 	}
24969610Sgdamore@opensolaris.org 
24979610Sgdamore@opensolaris.org 	/*
24989610Sgdamore@opensolaris.org 	 * DMA sync descriptors.
24999610Sgdamore@opensolaris.org 	 */
25009610Sgdamore@opensolaris.org 	(void) ddi_dma_sync(hmep->hme_rmd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
25019610Sgdamore@opensolaris.org 	(void) ddi_dma_sync(hmep->hme_tmd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
25029610Sgdamore@opensolaris.org 
25039610Sgdamore@opensolaris.org 	/*
25049610Sgdamore@opensolaris.org 	 * Reset RMD and TMD 'walking' pointers.
25059610Sgdamore@opensolaris.org 	 */
25069610Sgdamore@opensolaris.org 	hmep->hme_rxindex = 0;
25079610Sgdamore@opensolaris.org 	hmep->hme_txindex = hmep->hme_txreclaim = 0;
25089610Sgdamore@opensolaris.org 
25099610Sgdamore@opensolaris.org 	/*
25109610Sgdamore@opensolaris.org 	 * This is the right place to initialize MIF !!!
25119610Sgdamore@opensolaris.org 	 */
25129610Sgdamore@opensolaris.org 
25139610Sgdamore@opensolaris.org 	PUT_MIFREG(mif_imask, HME_MIF_INTMASK);	/* mask all interrupts */
25149610Sgdamore@opensolaris.org 
25159610Sgdamore@opensolaris.org 	if (!hmep->hme_frame_enable)
25169610Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, GET_MIFREG(mif_cfg) | HME_MIF_CFGBB);
25179610Sgdamore@opensolaris.org 	else
25189610Sgdamore@opensolaris.org 		PUT_MIFREG(mif_cfg, GET_MIFREG(mif_cfg) & ~HME_MIF_CFGBB);
25199610Sgdamore@opensolaris.org 						/* enable frame mode */
25209610Sgdamore@opensolaris.org 
25219610Sgdamore@opensolaris.org 	/*
25229610Sgdamore@opensolaris.org 	 * Depending on the transceiver detected, select the source
25239610Sgdamore@opensolaris.org 	 * of the clocks for the MAC. Without the clocks, TX_MAC does
25249610Sgdamore@opensolaris.org 	 * not reset. When the Global Reset is issued to the Sbus/FEPS
25259610Sgdamore@opensolaris.org 	 * ASIC, it selects Internal by default.
25269610Sgdamore@opensolaris.org 	 */
25279610Sgdamore@opensolaris.org 
252810806Sgdamore@opensolaris.org 	switch ((phyad = mii_get_addr(hmep->hme_mii))) {
252910806Sgdamore@opensolaris.org 	case -1:
25309610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, XCVR_MSG, no_xcvr_msg);
25319610Sgdamore@opensolaris.org 		goto init_fail;	/* abort initialization */
25329610Sgdamore@opensolaris.org 
253310806Sgdamore@opensolaris.org 	case HME_INTERNAL_PHYAD:
25349610Sgdamore@opensolaris.org 		PUT_MACREG(xifc, 0);
253510806Sgdamore@opensolaris.org 		break;
253610806Sgdamore@opensolaris.org 	case HME_EXTERNAL_PHYAD:
253710806Sgdamore@opensolaris.org 		/* Isolate the Int. xcvr */
25389610Sgdamore@opensolaris.org 		PUT_MACREG(xifc, BMAC_XIFC_MIIBUFDIS);
253910806Sgdamore@opensolaris.org 		break;
25409610Sgdamore@opensolaris.org 	}
254110806Sgdamore@opensolaris.org 
25429610Sgdamore@opensolaris.org 	hmep->inits++;
25439610Sgdamore@opensolaris.org 
25449610Sgdamore@opensolaris.org 	/*
25459610Sgdamore@opensolaris.org 	 * Initialize BigMAC registers.
25469610Sgdamore@opensolaris.org 	 * First set the tx enable bit in tx config reg to 0 and poll on
25479610Sgdamore@opensolaris.org 	 * it till it turns to 0. Same for rx config, hash and address
25489610Sgdamore@opensolaris.org 	 * filter reg.
25499610Sgdamore@opensolaris.org 	 * Here is the sequence per the spec.
25509610Sgdamore@opensolaris.org 	 * MADD2 - MAC Address 2
25519610Sgdamore@opensolaris.org 	 * MADD1 - MAC Address 1
25529610Sgdamore@opensolaris.org 	 * MADD0 - MAC Address 0
25539610Sgdamore@opensolaris.org 	 * HASH3, HASH2, HASH1, HASH0 for group address
25549610Sgdamore@opensolaris.org 	 * AFR2, AFR1, AFR0 and AFMR for address filter mask
25559610Sgdamore@opensolaris.org 	 * Program RXMIN and RXMAX for packet length if not 802.3
25569610Sgdamore@opensolaris.org 	 * RXCFG - Rx config for not stripping CRC
25579610Sgdamore@opensolaris.org 	 * XXX Anything else to hme configured in RXCFG
25589610Sgdamore@opensolaris.org 	 * IPG1, IPG2, ALIMIT, SLOT, PALEN, PAPAT, TXSFD, JAM, TXMAX, TXMIN
25599610Sgdamore@opensolaris.org 	 * if not 802.3 compliant
25609610Sgdamore@opensolaris.org 	 * XIF register for speed selection
25619610Sgdamore@opensolaris.org 	 * MASK  - Interrupt mask
25629610Sgdamore@opensolaris.org 	 * Set bit 0 of TXCFG
25639610Sgdamore@opensolaris.org 	 * Set bit 0 of RXCFG
25649610Sgdamore@opensolaris.org 	 */
25659610Sgdamore@opensolaris.org 
25669610Sgdamore@opensolaris.org 	/*
25679610Sgdamore@opensolaris.org 	 * Initialize the TX_MAC registers
25689610Sgdamore@opensolaris.org 	 * Initialization of jamsize to work around rx crc bug
25699610Sgdamore@opensolaris.org 	 */
25709610Sgdamore@opensolaris.org 	PUT_MACREG(jam, jamsize);
25719610Sgdamore@opensolaris.org 
25729610Sgdamore@opensolaris.org #ifdef	FEPS_URUN_BUG
25739610Sgdamore@opensolaris.org 	if (hme_urun_fix)
25749610Sgdamore@opensolaris.org 		PUT_MACREG(palen, hme_palen);
25759610Sgdamore@opensolaris.org #endif
25769610Sgdamore@opensolaris.org 
257710806Sgdamore@opensolaris.org 	PUT_MACREG(ipg1, hmep->hme_ipg1);
257810806Sgdamore@opensolaris.org 	PUT_MACREG(ipg2, hmep->hme_ipg2);
25799610Sgdamore@opensolaris.org 
25809610Sgdamore@opensolaris.org 	PUT_MACREG(rseed,
25819610Sgdamore@opensolaris.org 	    ((hmep->hme_ouraddr.ether_addr_octet[0] << 8) & 0x3) |
25829610Sgdamore@opensolaris.org 	    hmep->hme_ouraddr.ether_addr_octet[1]);
25839610Sgdamore@opensolaris.org 
25849610Sgdamore@opensolaris.org 	/* Initialize the RX_MAC registers */
25859610Sgdamore@opensolaris.org 
25869610Sgdamore@opensolaris.org 	/*
25879610Sgdamore@opensolaris.org 	 * Program BigMAC with local individual ethernet address.
25889610Sgdamore@opensolaris.org 	 */
25899610Sgdamore@opensolaris.org 	PUT_MACREG(madd2, (hmep->hme_ouraddr.ether_addr_octet[4] << 8) |
25909610Sgdamore@opensolaris.org 	    hmep->hme_ouraddr.ether_addr_octet[5]);
25919610Sgdamore@opensolaris.org 	PUT_MACREG(madd1, (hmep->hme_ouraddr.ether_addr_octet[2] << 8) |
25929610Sgdamore@opensolaris.org 	    hmep->hme_ouraddr.ether_addr_octet[3]);
25939610Sgdamore@opensolaris.org 	PUT_MACREG(madd0, (hmep->hme_ouraddr.ether_addr_octet[0] << 8) |
25949610Sgdamore@opensolaris.org 	    hmep->hme_ouraddr.ether_addr_octet[1]);
25959610Sgdamore@opensolaris.org 
25969610Sgdamore@opensolaris.org 	/*
25979610Sgdamore@opensolaris.org 	 * Set up multicast address filter by passing all multicast
25989610Sgdamore@opensolaris.org 	 * addresses through a crc generator, and then using the
25999610Sgdamore@opensolaris.org 	 * low order 6 bits as a index into the 64 bit logical
26009610Sgdamore@opensolaris.org 	 * address filter. The high order three bits select the word,
26019610Sgdamore@opensolaris.org 	 * while the rest of the bits select the bit within the word.
26029610Sgdamore@opensolaris.org 	 */
26039610Sgdamore@opensolaris.org 	PUT_MACREG(hash0, hmep->hme_ladrf[0]);
26049610Sgdamore@opensolaris.org 	PUT_MACREG(hash1, hmep->hme_ladrf[1]);
26059610Sgdamore@opensolaris.org 	PUT_MACREG(hash2, hmep->hme_ladrf[2]);
26069610Sgdamore@opensolaris.org 	PUT_MACREG(hash3, hmep->hme_ladrf[3]);
26079610Sgdamore@opensolaris.org 
26089610Sgdamore@opensolaris.org 	/*
26099610Sgdamore@opensolaris.org 	 * Configure parameters to support VLAN.  (VLAN encapsulation adds
26109610Sgdamore@opensolaris.org 	 * four bytes.)
26119610Sgdamore@opensolaris.org 	 */
26129610Sgdamore@opensolaris.org 	PUT_MACREG(txmax, ETHERMAX + ETHERFCSL + 4);
26139610Sgdamore@opensolaris.org 	PUT_MACREG(rxmax, ETHERMAX + ETHERFCSL + 4);
26149610Sgdamore@opensolaris.org 
26159610Sgdamore@opensolaris.org 	/*
26169610Sgdamore@opensolaris.org 	 * Initialize HME Global registers, ETX registers and ERX registers.
26179610Sgdamore@opensolaris.org 	 */
26189610Sgdamore@opensolaris.org 
26199610Sgdamore@opensolaris.org 	PUT_ETXREG(txring, hmep->hme_tmd_paddr);
26209610Sgdamore@opensolaris.org 	PUT_ERXREG(rxring, hmep->hme_rmd_paddr);
26219610Sgdamore@opensolaris.org 
26229610Sgdamore@opensolaris.org 	/*
26239610Sgdamore@opensolaris.org 	 * ERX registers can be written only if they have even no. of bits set.
26249610Sgdamore@opensolaris.org 	 * So, if the value written is not read back, set the lsb and write
26259610Sgdamore@opensolaris.org 	 * again.
26269610Sgdamore@opensolaris.org 	 * static	int	hme_erx_fix = 1;   : Use the fix for erx bug
26279610Sgdamore@opensolaris.org 	 */
26289610Sgdamore@opensolaris.org 	{
26299610Sgdamore@opensolaris.org 		uint32_t temp;
26309610Sgdamore@opensolaris.org 		temp  = hmep->hme_rmd_paddr;
26319610Sgdamore@opensolaris.org 
26329610Sgdamore@opensolaris.org 		if (GET_ERXREG(rxring) != temp)
26339610Sgdamore@opensolaris.org 			PUT_ERXREG(rxring, (temp | 4));
26349610Sgdamore@opensolaris.org 	}
26359610Sgdamore@opensolaris.org 
26369610Sgdamore@opensolaris.org 	PUT_GLOBREG(config, (hmep->hme_config |
26379610Sgdamore@opensolaris.org 	    (hmep->hme_64bit_xfer << HMEG_CONFIG_64BIT_SHIFT)));
26389610Sgdamore@opensolaris.org 
26399610Sgdamore@opensolaris.org 	/*
26409610Sgdamore@opensolaris.org 	 * Significant performance improvements can be achieved by
26419610Sgdamore@opensolaris.org 	 * disabling transmit interrupt. Thus TMD's are reclaimed only
26429610Sgdamore@opensolaris.org 	 * when we run out of them in hmestart().
26439610Sgdamore@opensolaris.org 	 */
26449610Sgdamore@opensolaris.org 	PUT_GLOBREG(intmask,
26459610Sgdamore@opensolaris.org 	    HMEG_MASK_INTR | HMEG_MASK_TINT | HMEG_MASK_TX_ALL);
26469610Sgdamore@opensolaris.org 
26479610Sgdamore@opensolaris.org 	PUT_ETXREG(txring_size, ((HME_TMDMAX -1)>> HMET_RINGSZ_SHIFT));
26489610Sgdamore@opensolaris.org 	PUT_ETXREG(config, (GET_ETXREG(config) | HMET_CONFIG_TXDMA_EN
26499610Sgdamore@opensolaris.org 	    | HMET_CONFIG_TXFIFOTH));
26509610Sgdamore@opensolaris.org 	/* get the rxring size bits */
26519610Sgdamore@opensolaris.org 	switch (HME_RMDMAX) {
26529610Sgdamore@opensolaris.org 	case 32:
26539610Sgdamore@opensolaris.org 		i = HMER_CONFIG_RXRINGSZ32;
26549610Sgdamore@opensolaris.org 		break;
26559610Sgdamore@opensolaris.org 	case 64:
26569610Sgdamore@opensolaris.org 		i = HMER_CONFIG_RXRINGSZ64;
26579610Sgdamore@opensolaris.org 		break;
26589610Sgdamore@opensolaris.org 	case 128:
26599610Sgdamore@opensolaris.org 		i = HMER_CONFIG_RXRINGSZ128;
26609610Sgdamore@opensolaris.org 		break;
26619610Sgdamore@opensolaris.org 	case 256:
26629610Sgdamore@opensolaris.org 		i = HMER_CONFIG_RXRINGSZ256;
26639610Sgdamore@opensolaris.org 		break;
26649610Sgdamore@opensolaris.org 	default:
26659610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
26669610Sgdamore@opensolaris.org 		    unk_rx_ringsz_msg);
26679610Sgdamore@opensolaris.org 		goto init_fail;
26689610Sgdamore@opensolaris.org 	}
26699610Sgdamore@opensolaris.org 	i |= (HME_FSTBYTE_OFFSET << HMER_CONFIG_FBO_SHIFT)
26709610Sgdamore@opensolaris.org 	    | HMER_CONFIG_RXDMA_EN;
26719610Sgdamore@opensolaris.org 
26729610Sgdamore@opensolaris.org 	/* h/w checks start offset in half words */
26739610Sgdamore@opensolaris.org 	i |= ((sizeof (struct ether_header) / 2) << HMER_RX_CSSTART_SHIFT);
26749610Sgdamore@opensolaris.org 
26759610Sgdamore@opensolaris.org 	PUT_ERXREG(config, i);
26769610Sgdamore@opensolaris.org 
26779610Sgdamore@opensolaris.org 	/*
26789610Sgdamore@opensolaris.org 	 * Bug related to the parity handling in ERX. When erxp-config is
26799610Sgdamore@opensolaris.org 	 * read back.
26809610Sgdamore@opensolaris.org 	 * Sbus/FEPS drives the parity bit. This value is used while
26819610Sgdamore@opensolaris.org 	 * writing again.
26829610Sgdamore@opensolaris.org 	 * This fixes the RECV problem in SS5.
26839610Sgdamore@opensolaris.org 	 * static	int	hme_erx_fix = 1;   : Use the fix for erx bug
26849610Sgdamore@opensolaris.org 	 */
26859610Sgdamore@opensolaris.org 	{
26869610Sgdamore@opensolaris.org 		uint32_t temp;
26879610Sgdamore@opensolaris.org 		temp = GET_ERXREG(config);
26889610Sgdamore@opensolaris.org 		PUT_ERXREG(config, i);
26899610Sgdamore@opensolaris.org 
26909610Sgdamore@opensolaris.org 		if (GET_ERXREG(config) != i)
26919610Sgdamore@opensolaris.org 			HME_FAULT_MSG4(hmep, SEVERITY_UNKNOWN, ERX_MSG,
26929610Sgdamore@opensolaris.org 			    "error:temp = %x erxp->config = %x, should be %x",
26939610Sgdamore@opensolaris.org 			    temp, GET_ERXREG(config), i);
26949610Sgdamore@opensolaris.org 	}
26959610Sgdamore@opensolaris.org 
26969610Sgdamore@opensolaris.org 	/*
26979610Sgdamore@opensolaris.org 	 * Set up the rxconfig, txconfig and seed register without enabling
26989610Sgdamore@opensolaris.org 	 * them the former two at this time
26999610Sgdamore@opensolaris.org 	 *
27009610Sgdamore@opensolaris.org 	 * BigMAC strips the CRC bytes by default. Since this is
27019610Sgdamore@opensolaris.org 	 * contrary to other pieces of hardware, this bit needs to
27029610Sgdamore@opensolaris.org 	 * enabled to tell BigMAC not to strip the CRC bytes.
27039610Sgdamore@opensolaris.org 	 * Do not filter this node's own packets.
27049610Sgdamore@opensolaris.org 	 */
27059610Sgdamore@opensolaris.org 
27069610Sgdamore@opensolaris.org 	if (hme_reject_own) {
27079610Sgdamore@opensolaris.org 		PUT_MACREG(rxcfg,
27089610Sgdamore@opensolaris.org 		    ((hmep->hme_promisc ? BMAC_RXCFG_PROMIS : 0) |
27099610Sgdamore@opensolaris.org 		    BMAC_RXCFG_MYOWN | BMAC_RXCFG_HASH));
27109610Sgdamore@opensolaris.org 	} else {
27119610Sgdamore@opensolaris.org 		PUT_MACREG(rxcfg,
27129610Sgdamore@opensolaris.org 		    ((hmep->hme_promisc ? BMAC_RXCFG_PROMIS : 0) |
27139610Sgdamore@opensolaris.org 		    BMAC_RXCFG_HASH));
27149610Sgdamore@opensolaris.org 	}
27159610Sgdamore@opensolaris.org 
27169610Sgdamore@opensolaris.org 	drv_usecwait(10);	/* wait after setting Hash Enable bit */
27179610Sgdamore@opensolaris.org 
271810806Sgdamore@opensolaris.org 	fdx = (mii_get_duplex(hmep->hme_mii) == LINK_DUPLEX_FULL);
271910806Sgdamore@opensolaris.org 
27209610Sgdamore@opensolaris.org 	if (hme_ngu_enable)
272110806Sgdamore@opensolaris.org 		PUT_MACREG(txcfg, (fdx ? BMAC_TXCFG_FDX : 0) |
27229610Sgdamore@opensolaris.org 		    BMAC_TXCFG_NGU);
27239610Sgdamore@opensolaris.org 	else
272410806Sgdamore@opensolaris.org 		PUT_MACREG(txcfg, (fdx ? BMAC_TXCFG_FDX: 0));
27259610Sgdamore@opensolaris.org 
27269610Sgdamore@opensolaris.org 	i = 0;
272710806Sgdamore@opensolaris.org 	if ((hmep->hme_lance_mode) && (hmep->hme_lance_mode_enable))
272810806Sgdamore@opensolaris.org 		i = ((hmep->hme_ipg0 & HME_MASK_5BIT) << BMAC_XIFC_IPG0_SHIFT)
27299610Sgdamore@opensolaris.org 		    | BMAC_XIFC_LANCE_ENAB;
273010806Sgdamore@opensolaris.org 	if (phyad == HME_INTERNAL_PHYAD)
27319610Sgdamore@opensolaris.org 		PUT_MACREG(xifc, i | (BMAC_XIFC_ENAB));
27329610Sgdamore@opensolaris.org 	else
27339610Sgdamore@opensolaris.org 		PUT_MACREG(xifc, i | (BMAC_XIFC_ENAB | BMAC_XIFC_MIIBUFDIS));
27349610Sgdamore@opensolaris.org 
27359610Sgdamore@opensolaris.org 	PUT_MACREG(rxcfg, GET_MACREG(rxcfg) | BMAC_RXCFG_ENAB);
27369610Sgdamore@opensolaris.org 	PUT_MACREG(txcfg, GET_MACREG(txcfg) | BMAC_TXCFG_ENAB);
27379610Sgdamore@opensolaris.org 
27389610Sgdamore@opensolaris.org 	hmep->hme_flags |= (HMERUNNING | HMEINITIALIZED);
27399610Sgdamore@opensolaris.org 	/*
27409610Sgdamore@opensolaris.org 	 * Update the interrupt mask : this will re-allow interrupts to occur
27419610Sgdamore@opensolaris.org 	 */
27429610Sgdamore@opensolaris.org 	PUT_GLOBREG(intmask, HMEG_MASK_INTR);
27439610Sgdamore@opensolaris.org 	mac_tx_update(hmep->hme_mh);
27449610Sgdamore@opensolaris.org 
27459610Sgdamore@opensolaris.org init_fail:
27469610Sgdamore@opensolaris.org 	/*
27479610Sgdamore@opensolaris.org 	 * Release the locks in reverse order
27489610Sgdamore@opensolaris.org 	 */
27499610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
27509610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_intrlock);
27519610Sgdamore@opensolaris.org 
27529610Sgdamore@opensolaris.org 	ret = !(hmep->hme_flags & HMERUNNING);
27539610Sgdamore@opensolaris.org 	if (ret) {
27549610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
27559610Sgdamore@opensolaris.org 		    init_fail_gen_msg);
27569610Sgdamore@opensolaris.org 	}
27579610Sgdamore@opensolaris.org 
27589610Sgdamore@opensolaris.org 	/*
27599610Sgdamore@opensolaris.org 	 * Hardware checks.
27609610Sgdamore@opensolaris.org 	 */
27619610Sgdamore@opensolaris.org 	CHECK_GLOBREG();
27629610Sgdamore@opensolaris.org 	CHECK_MIFREG();
27639610Sgdamore@opensolaris.org 	CHECK_MACREG();
27649610Sgdamore@opensolaris.org 	CHECK_ERXREG();
27659610Sgdamore@opensolaris.org 	CHECK_ETXREG();
27669610Sgdamore@opensolaris.org 
27679610Sgdamore@opensolaris.org init_exit:
27689610Sgdamore@opensolaris.org 	return (ret);
27699610Sgdamore@opensolaris.org }
27709610Sgdamore@opensolaris.org 
27719610Sgdamore@opensolaris.org /*
27729610Sgdamore@opensolaris.org  * Calculate the dvma burstsize by setting up a dvma temporarily.  Return
27739610Sgdamore@opensolaris.org  * 0 as burstsize upon failure as it signifies no burst size.
27749610Sgdamore@opensolaris.org  * Requests for 64-bit transfer setup, if the platform supports it.
27759610Sgdamore@opensolaris.org  * NOTE: Do not use ddi_dma_alloc_handle(9f) then ddi_dma_burstsize(9f),
27769610Sgdamore@opensolaris.org  * sun4u Ultra-2 incorrectly returns a 32bit transfer.
27779610Sgdamore@opensolaris.org  */
27789610Sgdamore@opensolaris.org static int
hmeburstsizes(struct hme * hmep)27799610Sgdamore@opensolaris.org hmeburstsizes(struct hme *hmep)
27809610Sgdamore@opensolaris.org {
27819610Sgdamore@opensolaris.org 	int burstsizes;
27829610Sgdamore@opensolaris.org 	ddi_dma_handle_t handle;
27839610Sgdamore@opensolaris.org 
27849610Sgdamore@opensolaris.org 	if (ddi_dma_alloc_handle(hmep->dip, &hme_dma_attr,
27859610Sgdamore@opensolaris.org 	    DDI_DMA_DONTWAIT, NULL, &handle)) {
27869610Sgdamore@opensolaris.org 		return (0);
27879610Sgdamore@opensolaris.org 	}
27889610Sgdamore@opensolaris.org 
27899610Sgdamore@opensolaris.org 	hmep->hme_burstsizes = burstsizes = ddi_dma_burstsizes(handle);
27909610Sgdamore@opensolaris.org 	ddi_dma_free_handle(&handle);
27919610Sgdamore@opensolaris.org 
27929610Sgdamore@opensolaris.org 	/*
27939610Sgdamore@opensolaris.org 	 * Use user-configurable parameter for enabling 64-bit transfers
27949610Sgdamore@opensolaris.org 	 */
27959610Sgdamore@opensolaris.org 	burstsizes = (hmep->hme_burstsizes >> 16);
27969610Sgdamore@opensolaris.org 	if (burstsizes)
27979610Sgdamore@opensolaris.org 		hmep->hme_64bit_xfer = hme_64bit_enable; /* user config value */
27989610Sgdamore@opensolaris.org 	else
27999610Sgdamore@opensolaris.org 		burstsizes = hmep->hme_burstsizes;
28009610Sgdamore@opensolaris.org 
28019610Sgdamore@opensolaris.org 	if (hmep->hme_cheerio_mode)
28029610Sgdamore@opensolaris.org 		hmep->hme_64bit_xfer = 0; /* Disable for cheerio */
28039610Sgdamore@opensolaris.org 
28049610Sgdamore@opensolaris.org 	if (burstsizes & 0x40)
28059610Sgdamore@opensolaris.org 		hmep->hme_config = HMEG_CONFIG_BURST64;
28069610Sgdamore@opensolaris.org 	else if (burstsizes & 0x20)
28079610Sgdamore@opensolaris.org 		hmep->hme_config = HMEG_CONFIG_BURST32;
28089610Sgdamore@opensolaris.org 	else
28099610Sgdamore@opensolaris.org 		hmep->hme_config = HMEG_CONFIG_BURST16;
28109610Sgdamore@opensolaris.org 
28119610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
28129610Sgdamore@opensolaris.org }
28139610Sgdamore@opensolaris.org 
28149610Sgdamore@opensolaris.org static int
hmeallocbuf(struct hme * hmep,hmebuf_t * buf,int dir)28159610Sgdamore@opensolaris.org hmeallocbuf(struct hme *hmep, hmebuf_t *buf, int dir)
28169610Sgdamore@opensolaris.org {
28179610Sgdamore@opensolaris.org 	ddi_dma_cookie_t	dmac;
28189610Sgdamore@opensolaris.org 	size_t			len;
28199610Sgdamore@opensolaris.org 	unsigned		ccnt;
28209610Sgdamore@opensolaris.org 
28219610Sgdamore@opensolaris.org 	if (ddi_dma_alloc_handle(hmep->dip, &hme_dma_attr,
28229610Sgdamore@opensolaris.org 	    DDI_DMA_DONTWAIT, NULL, &buf->dmah) != DDI_SUCCESS) {
28239610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
28249610Sgdamore@opensolaris.org 		    "cannot allocate buf dma handle - failed");
28259610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
28269610Sgdamore@opensolaris.org 	}
28279610Sgdamore@opensolaris.org 
28289610Sgdamore@opensolaris.org 	if (ddi_dma_mem_alloc(buf->dmah, ROUNDUP(HMEBUFSIZE, 512),
28299610Sgdamore@opensolaris.org 	    &hme_buf_attr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL,
28309610Sgdamore@opensolaris.org 	    &buf->kaddr, &len, &buf->acch) != DDI_SUCCESS) {
28319610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
28329610Sgdamore@opensolaris.org 		    "cannot allocate buf memory - failed");
28339610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
28349610Sgdamore@opensolaris.org 	}
28359610Sgdamore@opensolaris.org 
28369610Sgdamore@opensolaris.org 	if (ddi_dma_addr_bind_handle(buf->dmah, NULL, buf->kaddr,
28379610Sgdamore@opensolaris.org 	    len, dir | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
28389610Sgdamore@opensolaris.org 	    &dmac, &ccnt) != DDI_DMA_MAPPED) {
28399610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
28409610Sgdamore@opensolaris.org 		    "cannot map buf for dma - failed");
28419610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
28429610Sgdamore@opensolaris.org 	}
28439610Sgdamore@opensolaris.org 	buf->paddr = dmac.dmac_address;
28449610Sgdamore@opensolaris.org 
28459610Sgdamore@opensolaris.org 	/* apparently they don't handle multiple cookies */
28469610Sgdamore@opensolaris.org 	if (ccnt > 1) {
28479610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
28489610Sgdamore@opensolaris.org 		    "too many buf dma cookies");
28499610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
28509610Sgdamore@opensolaris.org 	}
28519610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
28529610Sgdamore@opensolaris.org }
28539610Sgdamore@opensolaris.org 
28549610Sgdamore@opensolaris.org static int
hmeallocbufs(struct hme * hmep)28559610Sgdamore@opensolaris.org hmeallocbufs(struct hme *hmep)
28569610Sgdamore@opensolaris.org {
28579610Sgdamore@opensolaris.org 	hmep->hme_tbuf = kmem_zalloc(HME_TMDMAX * sizeof (hmebuf_t), KM_SLEEP);
28589610Sgdamore@opensolaris.org 	hmep->hme_rbuf = kmem_zalloc(HME_RMDMAX * sizeof (hmebuf_t), KM_SLEEP);
28599610Sgdamore@opensolaris.org 
28609610Sgdamore@opensolaris.org 	/* Alloc RX buffers. */
28619610Sgdamore@opensolaris.org 	for (int i = 0; i < HME_RMDMAX; i++) {
28629610Sgdamore@opensolaris.org 		if (hmeallocbuf(hmep, &hmep->hme_rbuf[i], DDI_DMA_READ) !=
28639610Sgdamore@opensolaris.org 		    DDI_SUCCESS) {
28649610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
28659610Sgdamore@opensolaris.org 		}
28669610Sgdamore@opensolaris.org 	}
28679610Sgdamore@opensolaris.org 
28689610Sgdamore@opensolaris.org 	/* Alloc TX buffers. */
28699610Sgdamore@opensolaris.org 	for (int i = 0; i < HME_TMDMAX; i++) {
28709610Sgdamore@opensolaris.org 		if (hmeallocbuf(hmep, &hmep->hme_tbuf[i], DDI_DMA_WRITE) !=
28719610Sgdamore@opensolaris.org 		    DDI_SUCCESS) {
28729610Sgdamore@opensolaris.org 			return (DDI_FAILURE);
28739610Sgdamore@opensolaris.org 		}
28749610Sgdamore@opensolaris.org 	}
28759610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
28769610Sgdamore@opensolaris.org }
28779610Sgdamore@opensolaris.org 
28789610Sgdamore@opensolaris.org static void
hmefreebufs(struct hme * hmep)28799610Sgdamore@opensolaris.org hmefreebufs(struct hme *hmep)
28809610Sgdamore@opensolaris.org {
28819610Sgdamore@opensolaris.org 	int i;
28829610Sgdamore@opensolaris.org 
28839610Sgdamore@opensolaris.org 	if (hmep->hme_rbuf == NULL)
28849610Sgdamore@opensolaris.org 		return;
28859610Sgdamore@opensolaris.org 
28869610Sgdamore@opensolaris.org 	/*
28879610Sgdamore@opensolaris.org 	 * Free and unload pending xmit and recv buffers.
28889610Sgdamore@opensolaris.org 	 * Maintaining the 1-to-1 ordered sequence of
28899610Sgdamore@opensolaris.org 	 * We have written the routine to be idempotent.
28909610Sgdamore@opensolaris.org 	 */
28919610Sgdamore@opensolaris.org 
28929610Sgdamore@opensolaris.org 	for (i = 0; i < HME_TMDMAX; i++) {
28939610Sgdamore@opensolaris.org 		hmebuf_t *tbuf = &hmep->hme_tbuf[i];
28949610Sgdamore@opensolaris.org 		if (tbuf->paddr) {
28959610Sgdamore@opensolaris.org 			(void) ddi_dma_unbind_handle(tbuf->dmah);
28969610Sgdamore@opensolaris.org 		}
28979610Sgdamore@opensolaris.org 		if (tbuf->kaddr) {
28989610Sgdamore@opensolaris.org 			ddi_dma_mem_free(&tbuf->acch);
28999610Sgdamore@opensolaris.org 		}
29009610Sgdamore@opensolaris.org 		if (tbuf->dmah) {
29019610Sgdamore@opensolaris.org 			ddi_dma_free_handle(&tbuf->dmah);
29029610Sgdamore@opensolaris.org 		}
29039610Sgdamore@opensolaris.org 	}
29049610Sgdamore@opensolaris.org 	for (i = 0; i < HME_RMDMAX; i++) {
29059610Sgdamore@opensolaris.org 		hmebuf_t *rbuf = &hmep->hme_rbuf[i];
29069610Sgdamore@opensolaris.org 		if (rbuf->paddr) {
29079610Sgdamore@opensolaris.org 			(void) ddi_dma_unbind_handle(rbuf->dmah);
29089610Sgdamore@opensolaris.org 		}
29099610Sgdamore@opensolaris.org 		if (rbuf->kaddr) {
29109610Sgdamore@opensolaris.org 			ddi_dma_mem_free(&rbuf->acch);
29119610Sgdamore@opensolaris.org 		}
29129610Sgdamore@opensolaris.org 		if (rbuf->dmah) {
29139610Sgdamore@opensolaris.org 			ddi_dma_free_handle(&rbuf->dmah);
29149610Sgdamore@opensolaris.org 		}
29159610Sgdamore@opensolaris.org 	}
29169610Sgdamore@opensolaris.org 	kmem_free(hmep->hme_rbuf, HME_RMDMAX * sizeof (hmebuf_t));
29179610Sgdamore@opensolaris.org 	kmem_free(hmep->hme_tbuf, HME_TMDMAX * sizeof (hmebuf_t));
29189610Sgdamore@opensolaris.org }
29199610Sgdamore@opensolaris.org 
29209610Sgdamore@opensolaris.org /*
29219610Sgdamore@opensolaris.org  * Un-initialize (STOP) HME channel.
29229610Sgdamore@opensolaris.org  */
29239610Sgdamore@opensolaris.org static void
hmeuninit(struct hme * hmep)29249610Sgdamore@opensolaris.org hmeuninit(struct hme *hmep)
29259610Sgdamore@opensolaris.org {
29269610Sgdamore@opensolaris.org 	/*
29279610Sgdamore@opensolaris.org 	 * Allow up to 'HMEDRAINTIME' for pending xmit's to complete.
29289610Sgdamore@opensolaris.org 	 */
29299610Sgdamore@opensolaris.org 	HMEDELAY((hmep->hme_txindex == hmep->hme_txreclaim), HMEDRAINTIME);
29309610Sgdamore@opensolaris.org 
29319610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_intrlock);
29329610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_xmitlock);
29339610Sgdamore@opensolaris.org 
29349610Sgdamore@opensolaris.org 	hmep->hme_flags &= ~HMERUNNING;
29359610Sgdamore@opensolaris.org 
29369610Sgdamore@opensolaris.org 	(void) hmestop(hmep);
29379610Sgdamore@opensolaris.org 
29389610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_xmitlock);
29399610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_intrlock);
29409610Sgdamore@opensolaris.org }
29419610Sgdamore@opensolaris.org 
29429610Sgdamore@opensolaris.org /*
29439610Sgdamore@opensolaris.org  * Allocate CONSISTENT memory for rmds and tmds with appropriate alignment and
29449610Sgdamore@opensolaris.org  * map it in IO space. Allocate space for transmit and receive ddi_dma_handle
29459610Sgdamore@opensolaris.org  * structures to use the DMA interface.
29469610Sgdamore@opensolaris.org  */
29479610Sgdamore@opensolaris.org static int
hmeallocthings(struct hme * hmep)29489610Sgdamore@opensolaris.org hmeallocthings(struct hme *hmep)
29499610Sgdamore@opensolaris.org {
29509610Sgdamore@opensolaris.org 	int			size;
29519610Sgdamore@opensolaris.org 	int			rval;
29529610Sgdamore@opensolaris.org 	size_t			real_len;
29539610Sgdamore@opensolaris.org 	uint_t			cookiec;
29549610Sgdamore@opensolaris.org 	ddi_dma_cookie_t	dmac;
29559610Sgdamore@opensolaris.org 	dev_info_t		*dip = hmep->dip;
29569610Sgdamore@opensolaris.org 
29579610Sgdamore@opensolaris.org 	/*
29589610Sgdamore@opensolaris.org 	 * Allocate the TMD and RMD descriptors and extra for page alignment.
29599610Sgdamore@opensolaris.org 	 */
29609610Sgdamore@opensolaris.org 
29619610Sgdamore@opensolaris.org 	rval = ddi_dma_alloc_handle(dip, &hme_dma_attr, DDI_DMA_DONTWAIT, NULL,
29629610Sgdamore@opensolaris.org 	    &hmep->hme_rmd_dmah);
29639610Sgdamore@opensolaris.org 	if (rval != DDI_SUCCESS) {
29649610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
29659610Sgdamore@opensolaris.org 		    "cannot allocate rmd handle - failed");
29669610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
29679610Sgdamore@opensolaris.org 	}
29689610Sgdamore@opensolaris.org 	size = HME_RMDMAX * sizeof (struct hme_rmd);
29699610Sgdamore@opensolaris.org 	rval = ddi_dma_mem_alloc(hmep->hme_rmd_dmah, size,
29709610Sgdamore@opensolaris.org 	    &hmep->hme_dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
29719610Sgdamore@opensolaris.org 	    &hmep->hme_rmd_kaddr, &real_len, &hmep->hme_rmd_acch);
29729610Sgdamore@opensolaris.org 	if (rval != DDI_SUCCESS) {
29739610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
29749610Sgdamore@opensolaris.org 		    "cannot allocate rmd dma mem - failed");
29759610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
29769610Sgdamore@opensolaris.org 	}
29779610Sgdamore@opensolaris.org 	hmep->hme_rmdp = (void *)(hmep->hme_rmd_kaddr);
29789610Sgdamore@opensolaris.org 	rval = ddi_dma_addr_bind_handle(hmep->hme_rmd_dmah, NULL,
29799610Sgdamore@opensolaris.org 	    hmep->hme_rmd_kaddr, size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
29809610Sgdamore@opensolaris.org 	    DDI_DMA_DONTWAIT, NULL, &dmac, &cookiec);
29819610Sgdamore@opensolaris.org 	if (rval != DDI_DMA_MAPPED) {
29829610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
29839610Sgdamore@opensolaris.org 		    "cannot allocate rmd dma - failed");
29849610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
29859610Sgdamore@opensolaris.org 	}
29869610Sgdamore@opensolaris.org 	hmep->hme_rmd_paddr = dmac.dmac_address;
29879610Sgdamore@opensolaris.org 	if (cookiec != 1) {
29889610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
29899610Sgdamore@opensolaris.org 		    "too many rmd cookies - failed");
29909610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
29919610Sgdamore@opensolaris.org 	}
29929610Sgdamore@opensolaris.org 
29939610Sgdamore@opensolaris.org 	rval = ddi_dma_alloc_handle(dip, &hme_dma_attr, DDI_DMA_DONTWAIT, NULL,
29949610Sgdamore@opensolaris.org 	    &hmep->hme_tmd_dmah);
29959610Sgdamore@opensolaris.org 	if (rval != DDI_SUCCESS) {
29969610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
29979610Sgdamore@opensolaris.org 		    "cannot allocate tmd handle - failed");
29989610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
29999610Sgdamore@opensolaris.org 	}
30009610Sgdamore@opensolaris.org 	size = HME_TMDMAX * sizeof (struct hme_rmd);
30019610Sgdamore@opensolaris.org 	rval = ddi_dma_mem_alloc(hmep->hme_tmd_dmah, size,
30029610Sgdamore@opensolaris.org 	    &hmep->hme_dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
30039610Sgdamore@opensolaris.org 	    &hmep->hme_tmd_kaddr, &real_len, &hmep->hme_tmd_acch);
30049610Sgdamore@opensolaris.org 	if (rval != DDI_SUCCESS) {
30059610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
30069610Sgdamore@opensolaris.org 		    "cannot allocate tmd dma mem - failed");
30079610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
30089610Sgdamore@opensolaris.org 	}
30099610Sgdamore@opensolaris.org 	hmep->hme_tmdp = (void *)(hmep->hme_tmd_kaddr);
30109610Sgdamore@opensolaris.org 	rval = ddi_dma_addr_bind_handle(hmep->hme_tmd_dmah, NULL,
30119610Sgdamore@opensolaris.org 	    hmep->hme_tmd_kaddr, size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
30129610Sgdamore@opensolaris.org 	    DDI_DMA_DONTWAIT, NULL, &dmac, &cookiec);
30139610Sgdamore@opensolaris.org 	if (rval != DDI_DMA_MAPPED) {
30149610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
30159610Sgdamore@opensolaris.org 		    "cannot allocate tmd dma - failed");
30169610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
30179610Sgdamore@opensolaris.org 	}
30189610Sgdamore@opensolaris.org 	hmep->hme_tmd_paddr = dmac.dmac_address;
30199610Sgdamore@opensolaris.org 	if (cookiec != 1) {
30209610Sgdamore@opensolaris.org 		HME_FAULT_MSG1(hmep, SEVERITY_HIGH, INIT_MSG,
30219610Sgdamore@opensolaris.org 		    "too many tmd cookies - failed");
30229610Sgdamore@opensolaris.org 		return (DDI_FAILURE);
30239610Sgdamore@opensolaris.org 	}
30249610Sgdamore@opensolaris.org 
30259610Sgdamore@opensolaris.org 	return (DDI_SUCCESS);
30269610Sgdamore@opensolaris.org }
30279610Sgdamore@opensolaris.org 
30289610Sgdamore@opensolaris.org static void
hmefreethings(struct hme * hmep)30299610Sgdamore@opensolaris.org hmefreethings(struct hme *hmep)
30309610Sgdamore@opensolaris.org {
30319610Sgdamore@opensolaris.org 	if (hmep->hme_rmd_paddr) {
30329610Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(hmep->hme_rmd_dmah);
30339610Sgdamore@opensolaris.org 		hmep->hme_rmd_paddr = 0;
30349610Sgdamore@opensolaris.org 	}
30359610Sgdamore@opensolaris.org 	if (hmep->hme_rmd_acch)
30369610Sgdamore@opensolaris.org 		ddi_dma_mem_free(&hmep->hme_rmd_acch);
30379610Sgdamore@opensolaris.org 	if (hmep->hme_rmd_dmah)
30389610Sgdamore@opensolaris.org 		ddi_dma_free_handle(&hmep->hme_rmd_dmah);
30399610Sgdamore@opensolaris.org 
30409610Sgdamore@opensolaris.org 	if (hmep->hme_tmd_paddr) {
30419610Sgdamore@opensolaris.org 		(void) ddi_dma_unbind_handle(hmep->hme_tmd_dmah);
30429610Sgdamore@opensolaris.org 		hmep->hme_tmd_paddr = 0;
30439610Sgdamore@opensolaris.org 	}
30449610Sgdamore@opensolaris.org 	if (hmep->hme_tmd_acch)
30459610Sgdamore@opensolaris.org 		ddi_dma_mem_free(&hmep->hme_tmd_acch);
30469610Sgdamore@opensolaris.org 	if (hmep->hme_tmd_dmah)
30479610Sgdamore@opensolaris.org 		ddi_dma_free_handle(&hmep->hme_tmd_dmah);
30489610Sgdamore@opensolaris.org }
30499610Sgdamore@opensolaris.org 
30509610Sgdamore@opensolaris.org /*
30519610Sgdamore@opensolaris.org  *	First check to see if it our device interrupting.
30529610Sgdamore@opensolaris.org  */
30539610Sgdamore@opensolaris.org static uint_t
hmeintr(caddr_t arg)30549610Sgdamore@opensolaris.org hmeintr(caddr_t arg)
30559610Sgdamore@opensolaris.org {
30569610Sgdamore@opensolaris.org 	struct hme	*hmep = (void *)arg;
30579610Sgdamore@opensolaris.org 	uint32_t	hmesbits;
30589610Sgdamore@opensolaris.org 	uint32_t	serviced = DDI_INTR_UNCLAIMED;
30599610Sgdamore@opensolaris.org 	uint32_t	num_reads = 0;
30609610Sgdamore@opensolaris.org 	uint32_t	rflags;
30619610Sgdamore@opensolaris.org 	mblk_t		*mp, *head, **tail;
30629610Sgdamore@opensolaris.org 
30639610Sgdamore@opensolaris.org 
30649610Sgdamore@opensolaris.org 	head = NULL;
30659610Sgdamore@opensolaris.org 	tail = &head;
30669610Sgdamore@opensolaris.org 
30679610Sgdamore@opensolaris.org 	mutex_enter(&hmep->hme_intrlock);
30689610Sgdamore@opensolaris.org 
30699610Sgdamore@opensolaris.org 	/*
30709610Sgdamore@opensolaris.org 	 * The status register auto-clears on read except for
30719610Sgdamore@opensolaris.org 	 * MIF Interrupt bit
30729610Sgdamore@opensolaris.org 	 */
30739610Sgdamore@opensolaris.org 	hmesbits = GET_GLOBREG(status);
30749610Sgdamore@opensolaris.org 	CHECK_GLOBREG();
30759610Sgdamore@opensolaris.org 
30769610Sgdamore@opensolaris.org 	/*
30779610Sgdamore@opensolaris.org 	 * Note: TINT is sometimes enabled in thr hmereclaim()
30789610Sgdamore@opensolaris.org 	 */
30799610Sgdamore@opensolaris.org 
30809610Sgdamore@opensolaris.org 	/*
30819610Sgdamore@opensolaris.org 	 * Bugid 1227832 - to handle spurious interrupts on fusion systems.
30829610Sgdamore@opensolaris.org 	 * Claim the first interrupt after initialization
30839610Sgdamore@opensolaris.org 	 */
30849610Sgdamore@opensolaris.org 	if (hmep->hme_flags & HMEINITIALIZED) {
30859610Sgdamore@opensolaris.org 		hmep->hme_flags &= ~HMEINITIALIZED;
30869610Sgdamore@opensolaris.org 		serviced = DDI_INTR_CLAIMED;
30879610Sgdamore@opensolaris.org 	}
30889610Sgdamore@opensolaris.org 
30899610Sgdamore@opensolaris.org 	if ((hmesbits & (HMEG_STATUS_INTR | HMEG_STATUS_TINT)) == 0) {
30909610Sgdamore@opensolaris.org 						/* No interesting interrupt */
30919610Sgdamore@opensolaris.org 		if (hmep->hme_intrstats) {
30929610Sgdamore@opensolaris.org 			if (serviced == DDI_INTR_UNCLAIMED)
30939610Sgdamore@opensolaris.org 				KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
30949610Sgdamore@opensolaris.org 			else
30959610Sgdamore@opensolaris.org 				KIOIP->intrs[KSTAT_INTR_HARD]++;
30969610Sgdamore@opensolaris.org 		}
30979610Sgdamore@opensolaris.org 		mutex_exit(&hmep->hme_intrlock);
30989610Sgdamore@opensolaris.org 		return (serviced);
30999610Sgdamore@opensolaris.org 	}
31009610Sgdamore@opensolaris.org 
31019610Sgdamore@opensolaris.org 	serviced = DDI_INTR_CLAIMED;
31029610Sgdamore@opensolaris.org 
31039610Sgdamore@opensolaris.org 	if (!(hmep->hme_flags & HMERUNNING)) {
31049610Sgdamore@opensolaris.org 		if (hmep->hme_intrstats)
31059610Sgdamore@opensolaris.org 			KIOIP->intrs[KSTAT_INTR_HARD]++;
31069610Sgdamore@opensolaris.org 		mutex_exit(&hmep->hme_intrlock);
31079610Sgdamore@opensolaris.org 		hmeuninit(hmep);
31089610Sgdamore@opensolaris.org 		return (serviced);
31099610Sgdamore@opensolaris.org 	}
31109610Sgdamore@opensolaris.org 
31119610Sgdamore@opensolaris.org 	if (hmesbits & (HMEG_STATUS_FATAL_ERR | HMEG_STATUS_NONFATAL_ERR)) {
31129610Sgdamore@opensolaris.org 		if (hmesbits & HMEG_STATUS_FATAL_ERR) {
31139610Sgdamore@opensolaris.org 
31149610Sgdamore@opensolaris.org 			if (hmep->hme_intrstats)
31159610Sgdamore@opensolaris.org 				KIOIP->intrs[KSTAT_INTR_HARD]++;
31169610Sgdamore@opensolaris.org 			hme_fatal_err(hmep, hmesbits);
31179610Sgdamore@opensolaris.org 
31189610Sgdamore@opensolaris.org 			mutex_exit(&hmep->hme_intrlock);
31199610Sgdamore@opensolaris.org 			(void) hmeinit(hmep);
31209610Sgdamore@opensolaris.org 			return (serviced);
31219610Sgdamore@opensolaris.org 		}
31229610Sgdamore@opensolaris.org 		hme_nonfatal_err(hmep, hmesbits);
31239610Sgdamore@opensolaris.org 	}
31249610Sgdamore@opensolaris.org 
31259610Sgdamore@opensolaris.org 	if (hmesbits & (HMEG_STATUS_TX_ALL | HMEG_STATUS_TINT)) {
31269610Sgdamore@opensolaris.org 		mutex_enter(&hmep->hme_xmitlock);
31279610Sgdamore@opensolaris.org 
31289610Sgdamore@opensolaris.org 		hmereclaim(hmep);
31299610Sgdamore@opensolaris.org 		mutex_exit(&hmep->hme_xmitlock);
31309610Sgdamore@opensolaris.org 	}
31319610Sgdamore@opensolaris.org 
31329610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RINT) {
31339610Sgdamore@opensolaris.org 
31349610Sgdamore@opensolaris.org 		/*
31359610Sgdamore@opensolaris.org 		 * This dummy PIO is required to flush the SBus
31369610Sgdamore@opensolaris.org 		 * Bridge buffers in QFE.
31379610Sgdamore@opensolaris.org 		 */
31389610Sgdamore@opensolaris.org 		(void) GET_GLOBREG(config);
31399610Sgdamore@opensolaris.org 
31409610Sgdamore@opensolaris.org 		/*
31419610Sgdamore@opensolaris.org 		 * Loop through each RMD no more than once.
31429610Sgdamore@opensolaris.org 		 */
31439610Sgdamore@opensolaris.org 		while (num_reads++ < HME_RMDMAX) {
31449610Sgdamore@opensolaris.org 			hmebuf_t *rbuf;
31459610Sgdamore@opensolaris.org 			int rxptr;
31469610Sgdamore@opensolaris.org 
31479610Sgdamore@opensolaris.org 			rxptr = hmep->hme_rxindex % HME_RMDMAX;
31489610Sgdamore@opensolaris.org 			HMESYNCRMD(rxptr, DDI_DMA_SYNC_FORKERNEL);
31499610Sgdamore@opensolaris.org 
31509610Sgdamore@opensolaris.org 			rflags = GET_RMD_FLAGS(rxptr);
31519610Sgdamore@opensolaris.org 			if (rflags & HMERMD_OWN) {
31529610Sgdamore@opensolaris.org 				/*
31539610Sgdamore@opensolaris.org 				 * Chip still owns it.  We're done.
31549610Sgdamore@opensolaris.org 				 */
31559610Sgdamore@opensolaris.org 				break;
31569610Sgdamore@opensolaris.org 			}
31579610Sgdamore@opensolaris.org 
31589610Sgdamore@opensolaris.org 			/*
31599610Sgdamore@opensolaris.org 			 * Retrieve the packet.
31609610Sgdamore@opensolaris.org 			 */
31619610Sgdamore@opensolaris.org 			rbuf = &hmep->hme_rbuf[rxptr];
31629610Sgdamore@opensolaris.org 			mp = hmeread(hmep, rbuf, rflags);
31639610Sgdamore@opensolaris.org 
31649610Sgdamore@opensolaris.org 			/*
31659610Sgdamore@opensolaris.org 			 * Return ownership of the RMD.
31669610Sgdamore@opensolaris.org 			 */
31679610Sgdamore@opensolaris.org 			PUT_RMD(rxptr, rbuf->paddr);
31689610Sgdamore@opensolaris.org 			HMESYNCRMD(rxptr, DDI_DMA_SYNC_FORDEV);
31699610Sgdamore@opensolaris.org 
31709610Sgdamore@opensolaris.org 			if (mp != NULL) {
31719610Sgdamore@opensolaris.org 				*tail = mp;
31729610Sgdamore@opensolaris.org 				tail = &mp->b_next;
31739610Sgdamore@opensolaris.org 			}
31749610Sgdamore@opensolaris.org 
31759610Sgdamore@opensolaris.org 			/*
31769610Sgdamore@opensolaris.org 			 * Advance to the next RMD.
31779610Sgdamore@opensolaris.org 			 */
31789610Sgdamore@opensolaris.org 			hmep->hme_rxindex++;
31799610Sgdamore@opensolaris.org 		}
31809610Sgdamore@opensolaris.org 	}
31819610Sgdamore@opensolaris.org 
31829610Sgdamore@opensolaris.org 	if (hmep->hme_intrstats)
31839610Sgdamore@opensolaris.org 		KIOIP->intrs[KSTAT_INTR_HARD]++;
31849610Sgdamore@opensolaris.org 
31859610Sgdamore@opensolaris.org 	mutex_exit(&hmep->hme_intrlock);
31869610Sgdamore@opensolaris.org 
31879610Sgdamore@opensolaris.org 	if (head != NULL)
31889610Sgdamore@opensolaris.org 		mac_rx(hmep->hme_mh, NULL, head);
31899610Sgdamore@opensolaris.org 
31909610Sgdamore@opensolaris.org 	return (serviced);
31919610Sgdamore@opensolaris.org }
31929610Sgdamore@opensolaris.org 
31939610Sgdamore@opensolaris.org /*
31949610Sgdamore@opensolaris.org  * Transmit completion reclaiming.
31959610Sgdamore@opensolaris.org  */
31969610Sgdamore@opensolaris.org static void
hmereclaim(struct hme * hmep)31979610Sgdamore@opensolaris.org hmereclaim(struct hme *hmep)
31989610Sgdamore@opensolaris.org {
31999610Sgdamore@opensolaris.org 	boolean_t	reclaimed = B_FALSE;
32009610Sgdamore@opensolaris.org 
32019610Sgdamore@opensolaris.org 	/*
32029610Sgdamore@opensolaris.org 	 * Loop through each TMD.
32039610Sgdamore@opensolaris.org 	 */
32049610Sgdamore@opensolaris.org 	while (hmep->hme_txindex > hmep->hme_txreclaim) {
32059610Sgdamore@opensolaris.org 
32069610Sgdamore@opensolaris.org 		int		reclaim;
32079610Sgdamore@opensolaris.org 		uint32_t	flags;
32089610Sgdamore@opensolaris.org 
32099610Sgdamore@opensolaris.org 		reclaim = hmep->hme_txreclaim % HME_TMDMAX;
32109610Sgdamore@opensolaris.org 		HMESYNCTMD(reclaim, DDI_DMA_SYNC_FORKERNEL);
32119610Sgdamore@opensolaris.org 
32129610Sgdamore@opensolaris.org 		flags = GET_TMD_FLAGS(reclaim);
32139610Sgdamore@opensolaris.org 		if (flags & HMETMD_OWN) {
32149610Sgdamore@opensolaris.org 			/*
32159610Sgdamore@opensolaris.org 			 * Chip still owns it.  We're done.
32169610Sgdamore@opensolaris.org 			 */
32179610Sgdamore@opensolaris.org 			break;
32189610Sgdamore@opensolaris.org 		}
32199610Sgdamore@opensolaris.org 
32209610Sgdamore@opensolaris.org 		/*
32219610Sgdamore@opensolaris.org 		 * Count a chained packet only once.
32229610Sgdamore@opensolaris.org 		 */
32239610Sgdamore@opensolaris.org 		if (flags & HMETMD_SOP) {
32249610Sgdamore@opensolaris.org 			hmep->hme_opackets++;
32259610Sgdamore@opensolaris.org 		}
32269610Sgdamore@opensolaris.org 
32279610Sgdamore@opensolaris.org 		/*
32289610Sgdamore@opensolaris.org 		 * MIB II
32299610Sgdamore@opensolaris.org 		 */
32309610Sgdamore@opensolaris.org 		hmep->hme_obytes += flags & HMETMD_BUFSIZE;
32319610Sgdamore@opensolaris.org 
32329610Sgdamore@opensolaris.org 		reclaimed = B_TRUE;
32339610Sgdamore@opensolaris.org 		hmep->hme_txreclaim++;
32349610Sgdamore@opensolaris.org 	}
32359610Sgdamore@opensolaris.org 
32369610Sgdamore@opensolaris.org 	if (reclaimed) {
32379610Sgdamore@opensolaris.org 		/*
32389610Sgdamore@opensolaris.org 		 * we could reclaim some TMDs so turn off interrupts
32399610Sgdamore@opensolaris.org 		 */
32409610Sgdamore@opensolaris.org 		if (hmep->hme_wantw) {
32419610Sgdamore@opensolaris.org 			PUT_GLOBREG(intmask,
32429610Sgdamore@opensolaris.org 			    HMEG_MASK_INTR | HMEG_MASK_TINT |
32439610Sgdamore@opensolaris.org 			    HMEG_MASK_TX_ALL);
32449610Sgdamore@opensolaris.org 			hmep->hme_wantw = B_FALSE;
32459610Sgdamore@opensolaris.org 			mac_tx_update(hmep->hme_mh);
32469610Sgdamore@opensolaris.org 		}
32479610Sgdamore@opensolaris.org 	} else {
32489610Sgdamore@opensolaris.org 		/*
32499610Sgdamore@opensolaris.org 		 * enable TINTS: so that even if there is no further activity
32509610Sgdamore@opensolaris.org 		 * hmereclaim will get called
32519610Sgdamore@opensolaris.org 		 */
32529610Sgdamore@opensolaris.org 		if (hmep->hme_wantw)
32539610Sgdamore@opensolaris.org 			PUT_GLOBREG(intmask,
32549610Sgdamore@opensolaris.org 			    GET_GLOBREG(intmask) & ~HMEG_MASK_TX_ALL);
32559610Sgdamore@opensolaris.org 	}
32569610Sgdamore@opensolaris.org 	CHECK_GLOBREG();
32579610Sgdamore@opensolaris.org }
32589610Sgdamore@opensolaris.org 
32599610Sgdamore@opensolaris.org /*
32609610Sgdamore@opensolaris.org  * Handle interrupts for fatal errors
32619610Sgdamore@opensolaris.org  * Need reinitialization of the ENET channel.
32629610Sgdamore@opensolaris.org  */
32639610Sgdamore@opensolaris.org static void
hme_fatal_err(struct hme * hmep,uint_t hmesbits)32649610Sgdamore@opensolaris.org hme_fatal_err(struct hme *hmep, uint_t hmesbits)
32659610Sgdamore@opensolaris.org {
32669610Sgdamore@opensolaris.org 
32679610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_SLV_PAR_ERR) {
32689610Sgdamore@opensolaris.org 		hmep->hme_slvparerr++;
32699610Sgdamore@opensolaris.org 	}
32709610Sgdamore@opensolaris.org 
32719610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_SLV_ERR_ACK) {
32729610Sgdamore@opensolaris.org 		hmep->hme_slverrack++;
32739610Sgdamore@opensolaris.org 	}
32749610Sgdamore@opensolaris.org 
32759610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_TX_TAG_ERR) {
32769610Sgdamore@opensolaris.org 		hmep->hme_txtagerr++;
32779610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
32789610Sgdamore@opensolaris.org 	}
32799610Sgdamore@opensolaris.org 
32809610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_TX_PAR_ERR) {
32819610Sgdamore@opensolaris.org 		hmep->hme_txparerr++;
32829610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
32839610Sgdamore@opensolaris.org 	}
32849610Sgdamore@opensolaris.org 
32859610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_TX_LATE_ERR) {
32869610Sgdamore@opensolaris.org 		hmep->hme_txlaterr++;
32879610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
32889610Sgdamore@opensolaris.org 	}
32899610Sgdamore@opensolaris.org 
32909610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_TX_ERR_ACK) {
32919610Sgdamore@opensolaris.org 		hmep->hme_txerrack++;
32929610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
32939610Sgdamore@opensolaris.org 	}
32949610Sgdamore@opensolaris.org 
32959610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_EOP_ERR) {
32969610Sgdamore@opensolaris.org 		hmep->hme_eoperr++;
32979610Sgdamore@opensolaris.org 	}
32989610Sgdamore@opensolaris.org 
32999610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RX_TAG_ERR) {
33009610Sgdamore@opensolaris.org 		hmep->hme_rxtagerr++;
33019610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33029610Sgdamore@opensolaris.org 	}
33039610Sgdamore@opensolaris.org 
33049610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RX_PAR_ERR) {
33059610Sgdamore@opensolaris.org 		hmep->hme_rxparerr++;
33069610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33079610Sgdamore@opensolaris.org 	}
33089610Sgdamore@opensolaris.org 
33099610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RX_LATE_ERR) {
33109610Sgdamore@opensolaris.org 		hmep->hme_rxlaterr++;
33119610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33129610Sgdamore@opensolaris.org 	}
33139610Sgdamore@opensolaris.org 
33149610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RX_ERR_ACK) {
33159610Sgdamore@opensolaris.org 		hmep->hme_rxerrack++;
33169610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33179610Sgdamore@opensolaris.org 	}
33189610Sgdamore@opensolaris.org }
33199610Sgdamore@opensolaris.org 
33209610Sgdamore@opensolaris.org /*
33219610Sgdamore@opensolaris.org  * Handle interrupts regarding non-fatal errors.
33229610Sgdamore@opensolaris.org  */
33239610Sgdamore@opensolaris.org static void
hme_nonfatal_err(struct hme * hmep,uint_t hmesbits)33249610Sgdamore@opensolaris.org hme_nonfatal_err(struct hme *hmep, uint_t hmesbits)
33259610Sgdamore@opensolaris.org {
33269610Sgdamore@opensolaris.org 
33279610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RX_DROP) {
33289610Sgdamore@opensolaris.org 		hmep->hme_missed++;
33299610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33309610Sgdamore@opensolaris.org 	}
33319610Sgdamore@opensolaris.org 
33329610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_DEFTIMR_EXP) {
33339610Sgdamore@opensolaris.org 		hmep->hme_defer_xmts++;
33349610Sgdamore@opensolaris.org 	}
33359610Sgdamore@opensolaris.org 
33369610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_FSTCOLC_EXP) {
33379610Sgdamore@opensolaris.org 		hmep->hme_fstcol += 256;
33389610Sgdamore@opensolaris.org 	}
33399610Sgdamore@opensolaris.org 
33409610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_LATCOLC_EXP) {
33419610Sgdamore@opensolaris.org 		hmep->hme_tlcol += 256;
33429610Sgdamore@opensolaris.org 		hmep->hme_oerrors += 256;
33439610Sgdamore@opensolaris.org 	}
33449610Sgdamore@opensolaris.org 
33459610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_EXCOLC_EXP) {
33469610Sgdamore@opensolaris.org 		hmep->hme_excol += 256;
33479610Sgdamore@opensolaris.org 		hmep->hme_oerrors += 256;
33489610Sgdamore@opensolaris.org 	}
33499610Sgdamore@opensolaris.org 
33509610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_NRMCOLC_EXP) {
33519610Sgdamore@opensolaris.org 		hmep->hme_coll += 256;
33529610Sgdamore@opensolaris.org 	}
33539610Sgdamore@opensolaris.org 
33549610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_MXPKTSZ_ERR) {
33559610Sgdamore@opensolaris.org 		hmep->hme_babl++;
33569610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
33579610Sgdamore@opensolaris.org 	}
33589610Sgdamore@opensolaris.org 
33599610Sgdamore@opensolaris.org 	/*
33609610Sgdamore@opensolaris.org 	 * This error is fatal and the board needs to
33619610Sgdamore@opensolaris.org 	 * be reinitialized. Comments?
33629610Sgdamore@opensolaris.org 	 */
33639610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_TXFIFO_UNDR) {
33649610Sgdamore@opensolaris.org 		hmep->hme_uflo++;
33659610Sgdamore@opensolaris.org 		hmep->hme_oerrors++;
33669610Sgdamore@opensolaris.org 	}
33679610Sgdamore@opensolaris.org 
33689610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_SQE_TST_ERR) {
33699610Sgdamore@opensolaris.org 		hmep->hme_sqe_errors++;
33709610Sgdamore@opensolaris.org 	}
33719610Sgdamore@opensolaris.org 
33729610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RCV_CNT_EXP) {
33739610Sgdamore@opensolaris.org 		if (hmep->hme_rxcv_enable) {
33749610Sgdamore@opensolaris.org 			hmep->hme_cvc += 256;
33759610Sgdamore@opensolaris.org 		}
33769610Sgdamore@opensolaris.org 	}
33779610Sgdamore@opensolaris.org 
33789610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_RXFIFO_OVFL) {
33799610Sgdamore@opensolaris.org 		hmep->hme_oflo++;
33809610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
33819610Sgdamore@opensolaris.org 	}
33829610Sgdamore@opensolaris.org 
33839610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_LEN_CNT_EXP) {
33849610Sgdamore@opensolaris.org 		hmep->hme_lenerr += 256;
33859610Sgdamore@opensolaris.org 		hmep->hme_ierrors += 256;
33869610Sgdamore@opensolaris.org 	}
33879610Sgdamore@opensolaris.org 
33889610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_ALN_CNT_EXP) {
33899610Sgdamore@opensolaris.org 		hmep->hme_align_errors += 256;
33909610Sgdamore@opensolaris.org 		hmep->hme_ierrors += 256;
33919610Sgdamore@opensolaris.org 	}
33929610Sgdamore@opensolaris.org 
33939610Sgdamore@opensolaris.org 	if (hmesbits & HMEG_STATUS_CRC_CNT_EXP) {
33949610Sgdamore@opensolaris.org 		hmep->hme_fcs_errors += 256;
33959610Sgdamore@opensolaris.org 		hmep->hme_ierrors += 256;
33969610Sgdamore@opensolaris.org 	}
33979610Sgdamore@opensolaris.org }
33989610Sgdamore@opensolaris.org 
33999610Sgdamore@opensolaris.org static mblk_t *
hmeread(struct hme * hmep,hmebuf_t * rbuf,uint32_t rflags)34009610Sgdamore@opensolaris.org hmeread(struct hme *hmep, hmebuf_t *rbuf, uint32_t rflags)
34019610Sgdamore@opensolaris.org {
34029610Sgdamore@opensolaris.org 	mblk_t		*bp;
34039610Sgdamore@opensolaris.org 	uint32_t	len;
34049610Sgdamore@opensolaris.org 	t_uscalar_t	type;
34059610Sgdamore@opensolaris.org 
34069610Sgdamore@opensolaris.org 	len = (rflags & HMERMD_BUFSIZE) >> HMERMD_BUFSIZE_SHIFT;
34079610Sgdamore@opensolaris.org 
34089610Sgdamore@opensolaris.org 	/*
34099610Sgdamore@opensolaris.org 	 * Check for short packet
34109610Sgdamore@opensolaris.org 	 * and check for overflow packet also. The processing is the
34119610Sgdamore@opensolaris.org 	 * same for both the cases - reuse the buffer. Update the Buffer
34129610Sgdamore@opensolaris.org 	 * overflow counter.
34139610Sgdamore@opensolaris.org 	 */
34149610Sgdamore@opensolaris.org 	if ((len < ETHERMIN) || (rflags & HMERMD_OVFLOW) ||
34159610Sgdamore@opensolaris.org 	    (len > (ETHERMAX + 4))) {
34169610Sgdamore@opensolaris.org 		if (len < ETHERMIN)
34179610Sgdamore@opensolaris.org 			hmep->hme_runt++;
34189610Sgdamore@opensolaris.org 
34199610Sgdamore@opensolaris.org 		else {
34209610Sgdamore@opensolaris.org 			hmep->hme_buff++;
34219610Sgdamore@opensolaris.org 			hmep->hme_toolong_errors++;
34229610Sgdamore@opensolaris.org 		}
34239610Sgdamore@opensolaris.org 		hmep->hme_ierrors++;
34249610Sgdamore@opensolaris.org 		return (NULL);
34259610Sgdamore@opensolaris.org 	}
34269610Sgdamore@opensolaris.org 
34279610Sgdamore@opensolaris.org 	/*
34289610Sgdamore@opensolaris.org 	 * Sync the received buffer before looking at it.
34299610Sgdamore@opensolaris.org 	 */
34309610Sgdamore@opensolaris.org 
34319610Sgdamore@opensolaris.org 	(void) ddi_dma_sync(rbuf->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
34329610Sgdamore@opensolaris.org 
34339610Sgdamore@opensolaris.org 	/*
34349610Sgdamore@opensolaris.org 	 * copy the packet data and then recycle the descriptor.
34359610Sgdamore@opensolaris.org 	 */
34369610Sgdamore@opensolaris.org 
34379610Sgdamore@opensolaris.org 	if ((bp = allocb(len + HME_FSTBYTE_OFFSET, BPRI_HI)) == NULL) {
34389610Sgdamore@opensolaris.org 
34399610Sgdamore@opensolaris.org 		hmep->hme_allocbfail++;
34409610Sgdamore@opensolaris.org 		hmep->hme_norcvbuf++;
34419610Sgdamore@opensolaris.org 
34429610Sgdamore@opensolaris.org 		return (NULL);
34439610Sgdamore@opensolaris.org 	}
34449610Sgdamore@opensolaris.org 
34459610Sgdamore@opensolaris.org 	bcopy(rbuf->kaddr, bp->b_rptr, len + HME_FSTBYTE_OFFSET);
34469610Sgdamore@opensolaris.org 
34479610Sgdamore@opensolaris.org 	hmep->hme_ipackets++;
34489610Sgdamore@opensolaris.org 
34499610Sgdamore@opensolaris.org 	/*  Add the First Byte offset to the b_rptr and copy */
34509610Sgdamore@opensolaris.org 	bp->b_rptr += HME_FSTBYTE_OFFSET;
34519610Sgdamore@opensolaris.org 	bp->b_wptr = bp->b_rptr + len;
34529610Sgdamore@opensolaris.org 
34539610Sgdamore@opensolaris.org 	/*
34549610Sgdamore@opensolaris.org 	 * update MIB II statistics
34559610Sgdamore@opensolaris.org 	 */
34569610Sgdamore@opensolaris.org 	BUMP_InNUcast(hmep, bp->b_rptr);
34579610Sgdamore@opensolaris.org 	hmep->hme_rbytes += len;
34589610Sgdamore@opensolaris.org 
34599610Sgdamore@opensolaris.org 	type = get_ether_type(bp->b_rptr);
34609610Sgdamore@opensolaris.org 
34619610Sgdamore@opensolaris.org 	/*
34629610Sgdamore@opensolaris.org 	 * TCP partial checksum in hardware
34639610Sgdamore@opensolaris.org 	 */
34649610Sgdamore@opensolaris.org 	if (type == ETHERTYPE_IP || type == ETHERTYPE_IPV6) {
34659610Sgdamore@opensolaris.org 		uint16_t cksum = ~rflags & HMERMD_CKSUM;
34669610Sgdamore@opensolaris.org 		uint_t end = len - sizeof (struct ether_header);
346711878SVenu.Iyer@Sun.COM 		mac_hcksum_set(bp, 0, 0, end, htons(cksum), HCK_PARTIALCKSUM);
34689610Sgdamore@opensolaris.org 	}
34699610Sgdamore@opensolaris.org 
34709610Sgdamore@opensolaris.org 	return (bp);
34719610Sgdamore@opensolaris.org }
34729610Sgdamore@opensolaris.org 
34739610Sgdamore@opensolaris.org /*VARARGS*/
34749610Sgdamore@opensolaris.org static void
hme_fault_msg(struct hme * hmep,uint_t severity,msg_t type,char * fmt,...)34759610Sgdamore@opensolaris.org hme_fault_msg(struct hme *hmep, uint_t severity, msg_t type, char *fmt, ...)
34769610Sgdamore@opensolaris.org {
34779610Sgdamore@opensolaris.org 	char	msg_buffer[255];
34789610Sgdamore@opensolaris.org 	va_list	ap;
34799610Sgdamore@opensolaris.org 
34809610Sgdamore@opensolaris.org 	va_start(ap, fmt);
34819610Sgdamore@opensolaris.org 	(void) vsnprintf(msg_buffer, sizeof (msg_buffer), fmt, ap);
34829610Sgdamore@opensolaris.org 
34839610Sgdamore@opensolaris.org 	if (hmep == NULL) {
34849610Sgdamore@opensolaris.org 		cmn_err(CE_NOTE, "hme : %s", msg_buffer);
34859610Sgdamore@opensolaris.org 
34869610Sgdamore@opensolaris.org 	} else if (type == DISPLAY_MSG) {
34879610Sgdamore@opensolaris.org 		cmn_err(CE_CONT, "?%s%d : %s\n", ddi_driver_name(hmep->dip),
34889610Sgdamore@opensolaris.org 		    hmep->instance, msg_buffer);
34899610Sgdamore@opensolaris.org 	} else if (severity == SEVERITY_HIGH) {
34909610Sgdamore@opensolaris.org 		cmn_err(CE_WARN, "%s%d : %s, SEVERITY_HIGH, %s\n",
34919610Sgdamore@opensolaris.org 		    ddi_driver_name(hmep->dip), hmep->instance,
34929610Sgdamore@opensolaris.org 		    msg_buffer, msg_string[type]);
34939610Sgdamore@opensolaris.org 	} else {
34949610Sgdamore@opensolaris.org 		cmn_err(CE_CONT, "%s%d : %s\n", ddi_driver_name(hmep->dip),
34959610Sgdamore@opensolaris.org 		    hmep->instance, msg_buffer);
34969610Sgdamore@opensolaris.org 	}
34979610Sgdamore@opensolaris.org 	va_end(ap);
34989610Sgdamore@opensolaris.org }
34999610Sgdamore@opensolaris.org 
35009610Sgdamore@opensolaris.org /*
35019610Sgdamore@opensolaris.org  * if this is the first init do not bother to save the
35029610Sgdamore@opensolaris.org  * counters. They should be 0, but do not count on it.
35039610Sgdamore@opensolaris.org  */
35049610Sgdamore@opensolaris.org static void
hmesavecntrs(struct hme * hmep)35059610Sgdamore@opensolaris.org hmesavecntrs(struct hme *hmep)
35069610Sgdamore@opensolaris.org {
35079610Sgdamore@opensolaris.org 	uint32_t fecnt, aecnt, lecnt, rxcv;
35089610Sgdamore@opensolaris.org 	uint32_t ltcnt, excnt;
35099610Sgdamore@opensolaris.org 
35109610Sgdamore@opensolaris.org 	/* XXX What all gets added in ierrors and oerrors? */
35119610Sgdamore@opensolaris.org 	fecnt = GET_MACREG(fecnt);
35129610Sgdamore@opensolaris.org 	PUT_MACREG(fecnt, 0);
35139610Sgdamore@opensolaris.org 
35149610Sgdamore@opensolaris.org 	aecnt = GET_MACREG(aecnt);
35159610Sgdamore@opensolaris.org 	hmep->hme_align_errors += aecnt;
35169610Sgdamore@opensolaris.org 	PUT_MACREG(aecnt, 0);
35179610Sgdamore@opensolaris.org 
35189610Sgdamore@opensolaris.org 	lecnt = GET_MACREG(lecnt);
35199610Sgdamore@opensolaris.org 	hmep->hme_lenerr += lecnt;
35209610Sgdamore@opensolaris.org 	PUT_MACREG(lecnt, 0);
35219610Sgdamore@opensolaris.org 
35229610Sgdamore@opensolaris.org 	rxcv = GET_MACREG(rxcv);
35239610Sgdamore@opensolaris.org #ifdef HME_CODEVIOL_BUG
35249610Sgdamore@opensolaris.org 	/*
35259610Sgdamore@opensolaris.org 	 * Ignore rxcv errors for Sbus/FEPS 2.1 or earlier
35269610Sgdamore@opensolaris.org 	 */
35279610Sgdamore@opensolaris.org 	if (!hmep->hme_rxcv_enable) {
35289610Sgdamore@opensolaris.org 		rxcv = 0;
35299610Sgdamore@opensolaris.org 	}
35309610Sgdamore@opensolaris.org #endif
35319610Sgdamore@opensolaris.org 	hmep->hme_cvc += rxcv;
35329610Sgdamore@opensolaris.org 	PUT_MACREG(rxcv, 0);
35339610Sgdamore@opensolaris.org 
35349610Sgdamore@opensolaris.org 	ltcnt = GET_MACREG(ltcnt);
35359610Sgdamore@opensolaris.org 	hmep->hme_tlcol += ltcnt;
35369610Sgdamore@opensolaris.org 	PUT_MACREG(ltcnt, 0);
35379610Sgdamore@opensolaris.org 
35389610Sgdamore@opensolaris.org 	excnt = GET_MACREG(excnt);
35399610Sgdamore@opensolaris.org 	hmep->hme_excol += excnt;
35409610Sgdamore@opensolaris.org 	PUT_MACREG(excnt, 0);
35419610Sgdamore@opensolaris.org 
35429610Sgdamore@opensolaris.org 	hmep->hme_fcs_errors += fecnt;
35439610Sgdamore@opensolaris.org 	hmep->hme_ierrors += (fecnt + aecnt + lecnt);
35449610Sgdamore@opensolaris.org 	hmep->hme_oerrors += (ltcnt + excnt);
35459610Sgdamore@opensolaris.org 	hmep->hme_coll += (GET_MACREG(nccnt) + ltcnt);
35469610Sgdamore@opensolaris.org 
35479610Sgdamore@opensolaris.org 	PUT_MACREG(nccnt, 0);
35489610Sgdamore@opensolaris.org 	CHECK_MACREG();
35499610Sgdamore@opensolaris.org }
35509610Sgdamore@opensolaris.org 
35519610Sgdamore@opensolaris.org /*
35529610Sgdamore@opensolaris.org  * To set up the mac address for the network interface:
35539610Sgdamore@opensolaris.org  * The adapter card may support a local mac address which is published
35549610Sgdamore@opensolaris.org  * in a device node property "local-mac-address". This mac address is
35559610Sgdamore@opensolaris.org  * treated as the factory-installed mac address for DLPI interface.
35569610Sgdamore@opensolaris.org  * If the adapter firmware has used the device for diskless boot
35579610Sgdamore@opensolaris.org  * operation it publishes a property called "mac-address" for use by
35589610Sgdamore@opensolaris.org  * inetboot and the device driver.
35599610Sgdamore@opensolaris.org  * If "mac-address" is not found, the system options property
35609610Sgdamore@opensolaris.org  * "local-mac-address" is used to select the mac-address. If this option
35619610Sgdamore@opensolaris.org  * is set to "true", and "local-mac-address" has been found, then
35629610Sgdamore@opensolaris.org  * local-mac-address is used; otherwise the system mac address is used
35639610Sgdamore@opensolaris.org  * by calling the "localetheraddr()" function.
35649610Sgdamore@opensolaris.org  */
35659610Sgdamore@opensolaris.org static void
hme_setup_mac_address(struct hme * hmep,dev_info_t * dip)35669610Sgdamore@opensolaris.org hme_setup_mac_address(struct hme *hmep, dev_info_t *dip)
35679610Sgdamore@opensolaris.org {
35689610Sgdamore@opensolaris.org 	char	*prop;
35699610Sgdamore@opensolaris.org 	int	prop_len = sizeof (int);
35709610Sgdamore@opensolaris.org 
35719610Sgdamore@opensolaris.org 	hmep->hme_addrflags = 0;
35729610Sgdamore@opensolaris.org 
35739610Sgdamore@opensolaris.org 	/*
35749610Sgdamore@opensolaris.org 	 * Check if it is an adapter with its own local mac address
35759610Sgdamore@opensolaris.org 	 * If it is present, save it as the "factory-address"
35769610Sgdamore@opensolaris.org 	 * for this adapter.
35779610Sgdamore@opensolaris.org 	 */
35789610Sgdamore@opensolaris.org 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
35799610Sgdamore@opensolaris.org 	    "local-mac-address",
35809610Sgdamore@opensolaris.org 	    (caddr_t)&prop, &prop_len) == DDI_PROP_SUCCESS) {
35819610Sgdamore@opensolaris.org 		if (prop_len == ETHERADDRL) {
35829610Sgdamore@opensolaris.org 			hmep->hme_addrflags = HME_FACTADDR_PRESENT;
35839610Sgdamore@opensolaris.org 			ether_bcopy(prop, &hmep->hme_factaddr);
35849610Sgdamore@opensolaris.org 			HME_FAULT_MSG2(hmep, SEVERITY_NONE, DISPLAY_MSG,
35859610Sgdamore@opensolaris.org 			    "Local Ethernet address = %s",
35869610Sgdamore@opensolaris.org 			    ether_sprintf(&hmep->hme_factaddr));
35879610Sgdamore@opensolaris.org 		}
35889610Sgdamore@opensolaris.org 		kmem_free(prop, prop_len);
35899610Sgdamore@opensolaris.org 	}
35909610Sgdamore@opensolaris.org 
35919610Sgdamore@opensolaris.org 	/*
35929610Sgdamore@opensolaris.org 	 * Check if the adapter has published "mac-address" property.
35939610Sgdamore@opensolaris.org 	 * If it is present, use it as the mac address for this device.
35949610Sgdamore@opensolaris.org 	 */
35959610Sgdamore@opensolaris.org 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
35969610Sgdamore@opensolaris.org 	    "mac-address", (caddr_t)&prop, &prop_len) == DDI_PROP_SUCCESS) {
35979610Sgdamore@opensolaris.org 		if (prop_len >= ETHERADDRL) {
35989610Sgdamore@opensolaris.org 			ether_bcopy(prop, &hmep->hme_ouraddr);
35999610Sgdamore@opensolaris.org 			kmem_free(prop, prop_len);
36009610Sgdamore@opensolaris.org 			return;
36019610Sgdamore@opensolaris.org 		}
36029610Sgdamore@opensolaris.org 		kmem_free(prop, prop_len);
36039610Sgdamore@opensolaris.org 	}
36049610Sgdamore@opensolaris.org 
36059610Sgdamore@opensolaris.org #ifdef	__sparc
36069610Sgdamore@opensolaris.org 	/*
36079610Sgdamore@opensolaris.org 	 * On sparc, we might be able to use the mac address from the
36089610Sgdamore@opensolaris.org 	 * system.  However, on all other systems, we need to use the
36099610Sgdamore@opensolaris.org 	 * address from the PROM.
36109610Sgdamore@opensolaris.org 	 */
36119610Sgdamore@opensolaris.org 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, "local-mac-address?",
36129610Sgdamore@opensolaris.org 	    (caddr_t)&prop, &prop_len) == DDI_PROP_SUCCESS) {
36139610Sgdamore@opensolaris.org 		if ((strncmp("true", prop, prop_len) == 0) &&
36149610Sgdamore@opensolaris.org 		    (hmep->hme_addrflags & HME_FACTADDR_PRESENT)) {
36159610Sgdamore@opensolaris.org 			hmep->hme_addrflags |= HME_FACTADDR_USE;
36169610Sgdamore@opensolaris.org 			ether_bcopy(&hmep->hme_factaddr, &hmep->hme_ouraddr);
36179610Sgdamore@opensolaris.org 			kmem_free(prop, prop_len);
36189610Sgdamore@opensolaris.org 			HME_FAULT_MSG1(hmep, SEVERITY_NONE, DISPLAY_MSG,
36199610Sgdamore@opensolaris.org 			    "Using local MAC address");
36209610Sgdamore@opensolaris.org 			return;
36219610Sgdamore@opensolaris.org 		}
36229610Sgdamore@opensolaris.org 		kmem_free(prop, prop_len);
36239610Sgdamore@opensolaris.org 	}
36249610Sgdamore@opensolaris.org 
36259610Sgdamore@opensolaris.org 	/*
36269610Sgdamore@opensolaris.org 	 * Get the system ethernet address.
36279610Sgdamore@opensolaris.org 	 */
36289610Sgdamore@opensolaris.org 	(void) localetheraddr((struct ether_addr *)NULL, &hmep->hme_ouraddr);
36299610Sgdamore@opensolaris.org #else
36309610Sgdamore@opensolaris.org 	ether_bcopy(&hmep->hme_factaddr, &hmep->hme_ouraddr);
36319610Sgdamore@opensolaris.org #endif
36329610Sgdamore@opensolaris.org }
36339610Sgdamore@opensolaris.org 
36349610Sgdamore@opensolaris.org /* ARGSUSED */
36359610Sgdamore@opensolaris.org static void
hme_check_acc_handle(char * file,uint_t line,struct hme * hmep,ddi_acc_handle_t handle)36369610Sgdamore@opensolaris.org hme_check_acc_handle(char *file, uint_t line, struct hme *hmep,
36379610Sgdamore@opensolaris.org     ddi_acc_handle_t handle)
36389610Sgdamore@opensolaris.org {
36399610Sgdamore@opensolaris.org }
3640