xref: /illumos-gate/usr/src/uts/common/io/pcn/pcn.c (revision 438b5f69)
1*438b5f69SJason King /*
2*438b5f69SJason King  * Copyright (c) 2011 Jason King.
3*438b5f69SJason King  * Copyright (c) 2000 Berkeley Software Design, Inc.
4*438b5f69SJason King  * Copyright (c) 1997, 1998, 1999, 2000
5*438b5f69SJason King  *      Bill Paul <wpaul@osd.bsdi.com>.  All rights reserved.
6*438b5f69SJason King  *
7*438b5f69SJason King  * Redistribution and use in source and binary forms, with or without
8*438b5f69SJason King  * modification, are permitted provided that the following conditions
9*438b5f69SJason King  * are met:
10*438b5f69SJason King  * 1. Redistributions of source code must retain the above copyright
11*438b5f69SJason King  *    notice, this list of conditions and the following disclaimer.
12*438b5f69SJason King  * 2. Redistributions in binary form must reproduce the above copyright
13*438b5f69SJason King  *    notice, this list of conditions and the following disclaimer in the
14*438b5f69SJason King  *    documentation and/or other materials provided with the distribution.
15*438b5f69SJason King  * 3. All advertising materials mentioning features or use of this software
16*438b5f69SJason King  *    must display the following acknowledgement:
17*438b5f69SJason King  *      This product includes software developed by Bill Paul.
18*438b5f69SJason King  * 4. Neither the name of the author nor the names of any co-contributors
19*438b5f69SJason King  *    may be used to endorse or promote products derived from this software
20*438b5f69SJason King  *    without specific prior written permission.
21*438b5f69SJason King  *
22*438b5f69SJason King  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23*438b5f69SJason King  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*438b5f69SJason King  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*438b5f69SJason King  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26*438b5f69SJason King  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27*438b5f69SJason King  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28*438b5f69SJason King  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29*438b5f69SJason King  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30*438b5f69SJason King  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31*438b5f69SJason King  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32*438b5f69SJason King  * THE POSSIBILITY OF SUCH DAMAGE.
33*438b5f69SJason King  */
34*438b5f69SJason King 
35*438b5f69SJason King #include <sys/varargs.h>
36*438b5f69SJason King #include <sys/types.h>
37*438b5f69SJason King #include <sys/modctl.h>
38*438b5f69SJason King #include <sys/devops.h>
39*438b5f69SJason King #include <sys/stream.h>
40*438b5f69SJason King #include <sys/strsun.h>
41*438b5f69SJason King #include <sys/cmn_err.h>
42*438b5f69SJason King #include <sys/ethernet.h>
43*438b5f69SJason King #include <sys/kmem.h>
44*438b5f69SJason King #include <sys/crc32.h>
45*438b5f69SJason King #include <sys/mii.h>
46*438b5f69SJason King #include <sys/miiregs.h>
47*438b5f69SJason King #include <sys/mac.h>
48*438b5f69SJason King #include <sys/mac_ether.h>
49*438b5f69SJason King #include <sys/ddi.h>
50*438b5f69SJason King #include <sys/sunddi.h>
51*438b5f69SJason King #include <sys/vlan.h>
52*438b5f69SJason King #include <sys/pci.h>
53*438b5f69SJason King #include <sys/conf.h>
54*438b5f69SJason King 
55*438b5f69SJason King #include "pcn.h"
56*438b5f69SJason King #include "pcnimpl.h"
57*438b5f69SJason King 
58*438b5f69SJason King #define	ETHERVLANMTU	(ETHERMAX + 4)
59*438b5f69SJason King 
60*438b5f69SJason King #define	CSR_WRITE_4(pcnp, reg, val) \
61*438b5f69SJason King 	ddi_put32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg), val)
62*438b5f69SJason King 
63*438b5f69SJason King #define	CSR_WRITE_2(pcnp, reg, val) \
64*438b5f69SJason King 	ddi_put16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg), val)
65*438b5f69SJason King 
66*438b5f69SJason King #define	CSR_READ_4(pcnp, reg) \
67*438b5f69SJason King 	ddi_get32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg))
68*438b5f69SJason King 
69*438b5f69SJason King #define	CSR_READ_2(pcnp, reg) \
70*438b5f69SJason King 	ddi_get16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg))
71*438b5f69SJason King 
72*438b5f69SJason King #define	PCN_CSR_SETBIT(pcnp, reg, x) \
73*438b5f69SJason King 	pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) | (x))
74*438b5f69SJason King 
75*438b5f69SJason King #define	PCN_CSR_CLRBIT(pcnp, reg, x) \
76*438b5f69SJason King 	pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) & ~(x))
77*438b5f69SJason King 
78*438b5f69SJason King #define	PCN_BCR_SETBIT(pncp, reg, x) \
79*438b5f69SJason King 	pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) | (x))
80*438b5f69SJason King 
81*438b5f69SJason King #define	PCN_BCR_CLRBIT(pcnp, reg, x) \
82*438b5f69SJason King 	pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) & ~(x))
83*438b5f69SJason King 
84*438b5f69SJason King static int	pcn_attach(dev_info_t *, ddi_attach_cmd_t);
85*438b5f69SJason King static int	pcn_detach(dev_info_t *, ddi_detach_cmd_t);
86*438b5f69SJason King static int	pcn_ddi_resume(dev_info_t *);
87*438b5f69SJason King static int	pcn_quiesce(dev_info_t *);
88*438b5f69SJason King 
89*438b5f69SJason King static void	pcn_teardown(pcn_t *);
90*438b5f69SJason King 
91*438b5f69SJason King static int	pcn_m_unicast(void *, const uint8_t *);
92*438b5f69SJason King static int	pcn_m_multicast(void *, boolean_t, const uint8_t *);
93*438b5f69SJason King static int	pcn_m_promisc(void *, boolean_t);
94*438b5f69SJason King static mblk_t	*pcn_m_tx(void *, mblk_t *);
95*438b5f69SJason King static void	pcn_m_ioctl(void *, queue_t *, mblk_t *);
96*438b5f69SJason King static int	pcn_m_stat(void *, uint_t, uint64_t *);
97*438b5f69SJason King static int	pcn_m_start(void *);
98*438b5f69SJason King static void	pcn_m_stop(void *);
99*438b5f69SJason King static int	pcn_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
100*438b5f69SJason King     void *);
101*438b5f69SJason King static int	pcn_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
102*438b5f69SJason King     const void *);
103*438b5f69SJason King static void	pcn_m_propinfo(void *, const char *, mac_prop_id_t,
104*438b5f69SJason King     mac_prop_info_handle_t);
105*438b5f69SJason King static int	pcn_watchdog(pcn_t *);
106*438b5f69SJason King 
107*438b5f69SJason King static unsigned pcn_intr(caddr_t);
108*438b5f69SJason King 
109*438b5f69SJason King static uint16_t	pcn_mii_read(void *, uint8_t, uint8_t);
110*438b5f69SJason King static void	pcn_mii_write(void *, uint8_t, uint8_t, uint16_t);
111*438b5f69SJason King static void	pcn_mii_notify(void *, link_state_t);
112*438b5f69SJason King 
113*438b5f69SJason King static uint32_t	pcn_csr_read(pcn_t *, uint32_t);
114*438b5f69SJason King static uint16_t	pcn_csr_read16(pcn_t *, uint32_t);
115*438b5f69SJason King static void	pcn_csr_write(pcn_t *, uint32_t, uint32_t);
116*438b5f69SJason King 
117*438b5f69SJason King static uint32_t	pcn_bcr_read(pcn_t *, uint32_t);
118*438b5f69SJason King static uint16_t pcn_bcr_read16(pcn_t *, uint32_t);
119*438b5f69SJason King static void	pcn_bcr_write(pcn_t *, uint32_t, uint32_t);
120*438b5f69SJason King 
121*438b5f69SJason King static boolean_t	pcn_send(pcn_t *, mblk_t *);
122*438b5f69SJason King 
123*438b5f69SJason King static pcn_buf_t	*pcn_allocbuf(pcn_t *);
124*438b5f69SJason King static void		pcn_destroybuf(pcn_buf_t *);
125*438b5f69SJason King static int		pcn_allocrxring(pcn_t *);
126*438b5f69SJason King static int		pcn_alloctxring(pcn_t *);
127*438b5f69SJason King static void		pcn_freetxring(pcn_t *);
128*438b5f69SJason King static void		pcn_freerxring(pcn_t *);
129*438b5f69SJason King static void		pcn_resetrings(pcn_t *);
130*438b5f69SJason King static int		pcn_initialize(pcn_t *, boolean_t);
131*438b5f69SJason King static mblk_t 		*pcn_receive(pcn_t *);
132*438b5f69SJason King static void		pcn_resetall(pcn_t *);
133*438b5f69SJason King static void		pcn_startall(pcn_t *);
134*438b5f69SJason King static void		pcn_stopall(pcn_t *);
135*438b5f69SJason King static void		pcn_reclaim(pcn_t *);
136*438b5f69SJason King static void		pcn_getfactaddr(pcn_t *);
137*438b5f69SJason King static int		pcn_set_chipid(pcn_t *, uint32_t);
138*438b5f69SJason King static const pcn_type_t *pcn_match(uint16_t, uint16_t);
139*438b5f69SJason King static void		pcn_start_timer(pcn_t *);
140*438b5f69SJason King static void		pcn_stop_timer(pcn_t *);
141*438b5f69SJason King 
142*438b5f69SJason King static void		pcn_error(dev_info_t *, char *, ...);
143*438b5f69SJason King 
144*438b5f69SJason King void *pcn_ssp = NULL;
145*438b5f69SJason King 
146*438b5f69SJason King static uchar_t pcn_broadcast[ETHERADDRL] = {
147*438b5f69SJason King 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
148*438b5f69SJason King };
149*438b5f69SJason King 
150*438b5f69SJason King static const pcn_type_t pcn_devs[] = {
151*438b5f69SJason King 	{ PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" },
152*438b5f69SJason King 	{ PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" },
153*438b5f69SJason King 	{ 0, 0, NULL }
154*438b5f69SJason King };
155*438b5f69SJason King 
156*438b5f69SJason King static mii_ops_t pcn_mii_ops = {
157*438b5f69SJason King 	MII_OPS_VERSION,
158*438b5f69SJason King 	pcn_mii_read,
159*438b5f69SJason King 	pcn_mii_write,
160*438b5f69SJason King 	pcn_mii_notify,
161*438b5f69SJason King 	NULL
162*438b5f69SJason King };
163*438b5f69SJason King 
164*438b5f69SJason King static mac_callbacks_t pcn_m_callbacks = {
165*438b5f69SJason King 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
166*438b5f69SJason King 	pcn_m_stat,
167*438b5f69SJason King 	pcn_m_start,
168*438b5f69SJason King 	pcn_m_stop,
169*438b5f69SJason King 	pcn_m_promisc,
170*438b5f69SJason King 	pcn_m_multicast,
171*438b5f69SJason King 	pcn_m_unicast,
172*438b5f69SJason King 	pcn_m_tx,
173*438b5f69SJason King 	NULL,
174*438b5f69SJason King 	pcn_m_ioctl,
175*438b5f69SJason King 	NULL,		/* mc_getcapab */
176*438b5f69SJason King 	NULL,		/* mc_open */
177*438b5f69SJason King 	NULL,		/* mc_close */
178*438b5f69SJason King 	pcn_m_setprop,
179*438b5f69SJason King 	pcn_m_getprop,
180*438b5f69SJason King 	pcn_m_propinfo
181*438b5f69SJason King };
182*438b5f69SJason King 
183*438b5f69SJason King DDI_DEFINE_STREAM_OPS(pcn_devops, nulldev, nulldev, pcn_attach, pcn_detach,
184*438b5f69SJason King     nodev, NULL, D_MP, NULL, pcn_quiesce);
185*438b5f69SJason King 
186*438b5f69SJason King static struct modldrv pcn_modldrv = {
187*438b5f69SJason King 	&mod_driverops,
188*438b5f69SJason King 	"AMD PCnet",
189*438b5f69SJason King 	&pcn_devops
190*438b5f69SJason King };
191*438b5f69SJason King 
192*438b5f69SJason King static struct modlinkage pcn_modlinkage = {
193*438b5f69SJason King 	MODREV_1,
194*438b5f69SJason King 	{ &pcn_modldrv, NULL }
195*438b5f69SJason King };
196*438b5f69SJason King 
197*438b5f69SJason King static ddi_device_acc_attr_t pcn_devattr = {
198*438b5f69SJason King 	DDI_DEVICE_ATTR_V0,
199*438b5f69SJason King 	DDI_STRUCTURE_LE_ACC,
200*438b5f69SJason King 	DDI_STRICTORDER_ACC
201*438b5f69SJason King };
202*438b5f69SJason King 
203*438b5f69SJason King static ddi_device_acc_attr_t pcn_bufattr = {
204*438b5f69SJason King 	DDI_DEVICE_ATTR_V0,
205*438b5f69SJason King 	DDI_NEVERSWAP_ACC,
206*438b5f69SJason King 	DDI_STRICTORDER_ACC
207*438b5f69SJason King };
208*438b5f69SJason King 
209*438b5f69SJason King static ddi_dma_attr_t pcn_dma_attr = {
210*438b5f69SJason King 	DMA_ATTR_V0,		/* dm_attr_version */
211*438b5f69SJason King 	0,			/* dma_attr_addr_lo */
212*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
213*438b5f69SJason King 	0x7FFFFFFFU,		/* dma_attr_count_max */
214*438b5f69SJason King 	4,			/* dma_attr_align */
215*438b5f69SJason King 	0x3F,			/* dma_attr_burstsizes */
216*438b5f69SJason King 	1,			/* dma_attr_minxfer */
217*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
218*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_seg */
219*438b5f69SJason King 	1,			/* dma_attr_sgllen */
220*438b5f69SJason King 	1,			/* dma_attr_granular */
221*438b5f69SJason King 	0			/* dma_attr_flags */
222*438b5f69SJason King };
223*438b5f69SJason King 
224*438b5f69SJason King static ddi_dma_attr_t pcn_dmadesc_attr = {
225*438b5f69SJason King 	DMA_ATTR_V0,		/* dm_attr_version */
226*438b5f69SJason King 	0,			/* dma_attr_addr_lo */
227*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_addr_hi */
228*438b5f69SJason King 	0x7FFFFFFFU,		/* dma_attr_count_max */
229*438b5f69SJason King 	16,			/* dma_attr_align */
230*438b5f69SJason King 	0x3F,			/* dma_attr_burstsizes */
231*438b5f69SJason King 	1,			/* dma_attr_minxfer */
232*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_maxxfer */
233*438b5f69SJason King 	0xFFFFFFFFU,		/* dma_attr_seg */
234*438b5f69SJason King 	1,			/* dma_attr_sgllen */
235*438b5f69SJason King 	1,			/* dma_attr_granular */
236*438b5f69SJason King 	0			/* dma_attr_flags */
237*438b5f69SJason King };
238*438b5f69SJason King 
239*438b5f69SJason King /*
240*438b5f69SJason King  * DDI entry points
241*438b5f69SJason King  */
242*438b5f69SJason King int
_init(void)243*438b5f69SJason King _init(void)
244*438b5f69SJason King {
245*438b5f69SJason King 	int	rc;
246*438b5f69SJason King 
247*438b5f69SJason King 	if ((rc = ddi_soft_state_init(&pcn_ssp, sizeof (pcn_t), 1)) != 0)
248*438b5f69SJason King 		return (rc);
249*438b5f69SJason King 
250*438b5f69SJason King 	mac_init_ops(&pcn_devops, "pcn");
251*438b5f69SJason King 	if ((rc = mod_install(&pcn_modlinkage)) != DDI_SUCCESS) {
252*438b5f69SJason King 		mac_fini_ops(&pcn_devops);
253*438b5f69SJason King 		ddi_soft_state_fini(&pcn_ssp);
254*438b5f69SJason King 	}
255*438b5f69SJason King 	return (rc);
256*438b5f69SJason King }
257*438b5f69SJason King 
258*438b5f69SJason King int
_fini(void)259*438b5f69SJason King _fini(void)
260*438b5f69SJason King {
261*438b5f69SJason King 	int	rc;
262*438b5f69SJason King 
263*438b5f69SJason King 	if ((rc = mod_remove(&pcn_modlinkage)) == DDI_SUCCESS) {
264*438b5f69SJason King 		mac_fini_ops(&pcn_devops);
265*438b5f69SJason King 		ddi_soft_state_fini(&pcn_ssp);
266*438b5f69SJason King 	}
267*438b5f69SJason King 	return (rc);
268*438b5f69SJason King }
269*438b5f69SJason King 
270*438b5f69SJason King int
_info(struct modinfo * modinfop)271*438b5f69SJason King _info(struct modinfo *modinfop)
272*438b5f69SJason King {
273*438b5f69SJason King 	return (mod_info(&pcn_modlinkage, modinfop));
274*438b5f69SJason King }
275*438b5f69SJason King 
276*438b5f69SJason King int
pcn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)277*438b5f69SJason King pcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
278*438b5f69SJason King {
279*438b5f69SJason King 	pcn_t			*pcnp;
280*438b5f69SJason King 	mac_register_t		*macp;
281*438b5f69SJason King 	const pcn_type_t	*pcn_type;
282*438b5f69SJason King 	int			instance = ddi_get_instance(dip);
283*438b5f69SJason King 	int			rc;
284*438b5f69SJason King 	ddi_acc_handle_t	pci;
285*438b5f69SJason King 	uint16_t		venid;
286*438b5f69SJason King 	uint16_t		devid;
287*438b5f69SJason King 	uint16_t		svid;
288*438b5f69SJason King 	uint16_t		ssid;
289*438b5f69SJason King 
290*438b5f69SJason King 	switch (cmd) {
291*438b5f69SJason King 	case DDI_RESUME:
292*438b5f69SJason King 		return (pcn_ddi_resume(dip));
293*438b5f69SJason King 
294*438b5f69SJason King 	case DDI_ATTACH:
295*438b5f69SJason King 		break;
296*438b5f69SJason King 
297*438b5f69SJason King 	default:
298*438b5f69SJason King 		return (DDI_FAILURE);
299*438b5f69SJason King 	}
300*438b5f69SJason King 
301*438b5f69SJason King 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
302*438b5f69SJason King 		pcn_error(dip, "slot does not support PCI bus-master");
303*438b5f69SJason King 		return (DDI_FAILURE);
304*438b5f69SJason King 	}
305*438b5f69SJason King 
306*438b5f69SJason King 	if (ddi_intr_hilevel(dip, 0) != 0) {
307*438b5f69SJason King 		pcn_error(dip, "hilevel interrupts not supported");
308*438b5f69SJason King 		return (DDI_FAILURE);
309*438b5f69SJason King 	}
310*438b5f69SJason King 
311*438b5f69SJason King 	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
312*438b5f69SJason King 		pcn_error(dip, "unable to setup PCI config handle");
313*438b5f69SJason King 		return (DDI_FAILURE);
314*438b5f69SJason King 	}
315*438b5f69SJason King 
316*438b5f69SJason King 	venid = pci_config_get16(pci, PCI_CONF_VENID);
317*438b5f69SJason King 	devid = pci_config_get16(pci, PCI_CONF_DEVID);
318*438b5f69SJason King 	svid = pci_config_get16(pci, PCI_CONF_SUBVENID);
319*438b5f69SJason King 	ssid = pci_config_get16(pci, PCI_CONF_SUBSYSID);
320*438b5f69SJason King 
321*438b5f69SJason King 	if ((pcn_type = pcn_match(venid, devid)) == NULL) {
322*438b5f69SJason King 		pci_config_teardown(&pci);
323*438b5f69SJason King 		pcn_error(dip, "Unable to identify PCI card");
324*438b5f69SJason King 		return (DDI_FAILURE);
325*438b5f69SJason King 	}
326*438b5f69SJason King 
327*438b5f69SJason King 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
328*438b5f69SJason King 	    pcn_type->pcn_name) != DDI_PROP_SUCCESS) {
329*438b5f69SJason King 		pci_config_teardown(&pci);
330*438b5f69SJason King 		pcn_error(dip, "Unable to create model property");
331*438b5f69SJason King 		return (DDI_FAILURE);
332*438b5f69SJason King 	}
333*438b5f69SJason King 
334*438b5f69SJason King 	if (ddi_soft_state_zalloc(pcn_ssp, instance) != DDI_SUCCESS) {
335*438b5f69SJason King 		pcn_error(dip, "Unable to allocate soft state");
336*438b5f69SJason King 		pci_config_teardown(&pci);
337*438b5f69SJason King 		return (DDI_FAILURE);
338*438b5f69SJason King 	}
339*438b5f69SJason King 
340*438b5f69SJason King 	pcnp = ddi_get_soft_state(pcn_ssp, instance);
341*438b5f69SJason King 	pcnp->pcn_dip = dip;
342*438b5f69SJason King 	pcnp->pcn_instance = instance;
343*438b5f69SJason King 	pcnp->pcn_extphyaddr = -1;
344*438b5f69SJason King 
345*438b5f69SJason King 	if (ddi_get_iblock_cookie(dip, 0, &pcnp->pcn_icookie) != DDI_SUCCESS) {
346*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "ddi_get_iblock_cookie failed");
347*438b5f69SJason King 		ddi_soft_state_free(pcn_ssp, instance);
348*438b5f69SJason King 		pci_config_teardown(&pci);
349*438b5f69SJason King 		return (DDI_FAILURE);
350*438b5f69SJason King 	}
351*438b5f69SJason King 
352*438b5f69SJason King 
353*438b5f69SJason King 	mutex_init(&pcnp->pcn_xmtlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
354*438b5f69SJason King 	mutex_init(&pcnp->pcn_intrlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
355*438b5f69SJason King 	mutex_init(&pcnp->pcn_reglock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
356*438b5f69SJason King 
357*438b5f69SJason King 	/*
358*438b5f69SJason King 	 * Enable bus master, IO space, and memory space accesses
359*438b5f69SJason King 	 */
360*438b5f69SJason King 	pci_config_put16(pci, PCI_CONF_COMM,
361*438b5f69SJason King 	    pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_ME | PCI_COMM_MAE);
362*438b5f69SJason King 
363*438b5f69SJason King 	pci_config_teardown(&pci);
364*438b5f69SJason King 
365*438b5f69SJason King 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcnp->pcn_regs, 0, 0,
366*438b5f69SJason King 	    &pcn_devattr, &pcnp->pcn_regshandle)) {
367*438b5f69SJason King 		pcn_error(dip, "ddi_regs_map_setup failed");
368*438b5f69SJason King 		goto fail;
369*438b5f69SJason King 	}
370*438b5f69SJason King 
371*438b5f69SJason King 	if (pcn_set_chipid(pcnp, (uint32_t)ssid << 16 | (uint32_t)svid) !=
372*438b5f69SJason King 	    DDI_SUCCESS) {
373*438b5f69SJason King 		goto fail;
374*438b5f69SJason King 	}
375*438b5f69SJason King 
376*438b5f69SJason King 	if ((pcnp->pcn_mii = mii_alloc(pcnp, dip, &pcn_mii_ops)) == NULL)
377*438b5f69SJason King 		goto fail;
378*438b5f69SJason King 
379*438b5f69SJason King 	/* XXX: need to set based on device */
380*438b5f69SJason King 	mii_set_pauseable(pcnp->pcn_mii, B_FALSE, B_FALSE);
381*438b5f69SJason King 
382*438b5f69SJason King 	if ((pcn_allocrxring(pcnp) != DDI_SUCCESS) ||
383*438b5f69SJason King 	    (pcn_alloctxring(pcnp) != DDI_SUCCESS)) {
384*438b5f69SJason King 		pcn_error(dip, "unable to allocate DMA resources");
385*438b5f69SJason King 		goto fail;
386*438b5f69SJason King 	}
387*438b5f69SJason King 
388*438b5f69SJason King 	pcnp->pcn_promisc = B_FALSE;
389*438b5f69SJason King 
390*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
391*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
392*438b5f69SJason King 	rc = pcn_initialize(pcnp, B_TRUE);
393*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
394*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
395*438b5f69SJason King 	if (rc != DDI_SUCCESS)
396*438b5f69SJason King 		goto fail;
397*438b5f69SJason King 
398*438b5f69SJason King 	if (ddi_add_intr(dip, 0, NULL, NULL, pcn_intr, (caddr_t)pcnp) !=
399*438b5f69SJason King 	    DDI_SUCCESS) {
400*438b5f69SJason King 		pcn_error(dip, "unable to add interrupt");
401*438b5f69SJason King 		goto fail;
402*438b5f69SJason King 	}
403*438b5f69SJason King 
404*438b5f69SJason King 	pcnp->pcn_flags |= PCN_INTR_ENABLED;
405*438b5f69SJason King 
406*438b5f69SJason King 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
407*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "mac_alloc failed");
408*438b5f69SJason King 		goto fail;
409*438b5f69SJason King 	}
410*438b5f69SJason King 
411*438b5f69SJason King 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
412*438b5f69SJason King 	macp->m_driver = pcnp;
413*438b5f69SJason King 	macp->m_dip = dip;
414*438b5f69SJason King 	macp->m_src_addr = pcnp->pcn_addr;
415*438b5f69SJason King 	macp->m_callbacks = &pcn_m_callbacks;
416*438b5f69SJason King 	macp->m_min_sdu = 0;
417*438b5f69SJason King 	macp->m_max_sdu = ETHERMTU;
418*438b5f69SJason King 	macp->m_margin = VLAN_TAGSZ;
419*438b5f69SJason King 
420*438b5f69SJason King 	if (mac_register(macp, &pcnp->pcn_mh) == DDI_SUCCESS) {
421*438b5f69SJason King 		mac_free(macp);
422*438b5f69SJason King 		return (DDI_SUCCESS);
423*438b5f69SJason King 	}
424*438b5f69SJason King 
425*438b5f69SJason King 	mac_free(macp);
426*438b5f69SJason King 
427*438b5f69SJason King 	return (DDI_SUCCESS);
428*438b5f69SJason King 
429*438b5f69SJason King fail:
430*438b5f69SJason King 	pcn_teardown(pcnp);
431*438b5f69SJason King 	return (DDI_FAILURE);
432*438b5f69SJason King }
433*438b5f69SJason King 
434*438b5f69SJason King int
pcn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)435*438b5f69SJason King pcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
436*438b5f69SJason King {
437*438b5f69SJason King 	pcn_t	*pcnp;
438*438b5f69SJason King 
439*438b5f69SJason King 	pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
440*438b5f69SJason King 
441*438b5f69SJason King 	if (pcnp == NULL) {
442*438b5f69SJason King 		pcn_error(dip, "no soft state in detach!");
443*438b5f69SJason King 		return (DDI_FAILURE);
444*438b5f69SJason King 	}
445*438b5f69SJason King 
446*438b5f69SJason King 	switch (cmd) {
447*438b5f69SJason King 	case DDI_DETACH:
448*438b5f69SJason King 		if (mac_unregister(pcnp->pcn_mh) != 0)
449*438b5f69SJason King 			return (DDI_FAILURE);
450*438b5f69SJason King 
451*438b5f69SJason King 		mutex_enter(&pcnp->pcn_intrlock);
452*438b5f69SJason King 		mutex_enter(&pcnp->pcn_xmtlock);
453*438b5f69SJason King 		pcnp->pcn_flags &= ~PCN_RUNNING;
454*438b5f69SJason King 		pcn_stopall(pcnp);
455*438b5f69SJason King 		mutex_exit(&pcnp->pcn_xmtlock);
456*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
457*438b5f69SJason King 
458*438b5f69SJason King 		pcn_teardown(pcnp);
459*438b5f69SJason King 		return (DDI_SUCCESS);
460*438b5f69SJason King 
461*438b5f69SJason King 	case DDI_SUSPEND:
462*438b5f69SJason King 		mii_suspend(pcnp->pcn_mii);
463*438b5f69SJason King 
464*438b5f69SJason King 		mutex_enter(&pcnp->pcn_intrlock);
465*438b5f69SJason King 		mutex_enter(&pcnp->pcn_xmtlock);
466*438b5f69SJason King 		pcnp->pcn_flags |= PCN_SUSPENDED;
467*438b5f69SJason King 		pcn_stopall(pcnp);
468*438b5f69SJason King 		mutex_exit(&pcnp->pcn_xmtlock);
469*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
470*438b5f69SJason King 		return (DDI_SUCCESS);
471*438b5f69SJason King 
472*438b5f69SJason King 	default:
473*438b5f69SJason King 		return (DDI_FAILURE);
474*438b5f69SJason King 	}
475*438b5f69SJason King }
476*438b5f69SJason King 
477*438b5f69SJason King int
pcn_ddi_resume(dev_info_t * dip)478*438b5f69SJason King pcn_ddi_resume(dev_info_t *dip)
479*438b5f69SJason King {
480*438b5f69SJason King 	pcn_t	*pcnp;
481*438b5f69SJason King 
482*438b5f69SJason King 	if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
483*438b5f69SJason King 		return (DDI_FAILURE);
484*438b5f69SJason King 
485*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
486*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
487*438b5f69SJason King 
488*438b5f69SJason King 	pcnp->pcn_flags &= ~PCN_SUSPENDED;
489*438b5f69SJason King 
490*438b5f69SJason King 	if (!pcn_initialize(pcnp, B_FALSE)) {
491*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to resume chip");
492*438b5f69SJason King 		pcnp->pcn_flags |= PCN_SUSPENDED;
493*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
494*438b5f69SJason King 		mutex_exit(&pcnp->pcn_xmtlock);
495*438b5f69SJason King 		return (DDI_SUCCESS);
496*438b5f69SJason King 	}
497*438b5f69SJason King 
498*438b5f69SJason King 	if (IS_RUNNING(pcnp))
499*438b5f69SJason King 		pcn_startall(pcnp);
500*438b5f69SJason King 
501*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
502*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
503*438b5f69SJason King 
504*438b5f69SJason King 	mii_resume(pcnp->pcn_mii);
505*438b5f69SJason King 
506*438b5f69SJason King 	return (DDI_SUCCESS);
507*438b5f69SJason King }
508*438b5f69SJason King 
509*438b5f69SJason King int
pcn_quiesce(dev_info_t * dip)510*438b5f69SJason King pcn_quiesce(dev_info_t *dip)
511*438b5f69SJason King {
512*438b5f69SJason King 	pcn_t	*pcnp;
513*438b5f69SJason King 
514*438b5f69SJason King 	if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
515*438b5f69SJason King 		return (DDI_FAILURE);
516*438b5f69SJason King 
517*438b5f69SJason King 	/* don't want to take the chance of blocking */
518*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_EXTCTL1);
519*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RDP, CSR_READ_4(pcnp, PCN_IO32_RDP) &
520*438b5f69SJason King 	    ~(PCN_EXTCTL1_SINTEN));
521*438b5f69SJason King 
522*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_CSR);
523*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RDP,
524*438b5f69SJason King 	    (CSR_READ_4(pcnp, PCN_IO32_RDP) & ~(PCN_CSR_INTEN)) |
525*438b5f69SJason King 	    PCN_CSR_STOP);
526*438b5f69SJason King 
527*438b5f69SJason King 	return (DDI_SUCCESS);
528*438b5f69SJason King }
529*438b5f69SJason King 
530*438b5f69SJason King static void
pcn_teardown(pcn_t * pcnp)531*438b5f69SJason King pcn_teardown(pcn_t *pcnp)
532*438b5f69SJason King {
533*438b5f69SJason King 	ASSERT(!(pcnp->pcn_flags & PCN_RUNNING));
534*438b5f69SJason King 
535*438b5f69SJason King 	if (pcnp->pcn_mii != NULL) {
536*438b5f69SJason King 		mii_free(pcnp->pcn_mii);
537*438b5f69SJason King 		pcnp->pcn_mii = NULL;
538*438b5f69SJason King 	}
539*438b5f69SJason King 
540*438b5f69SJason King 	if (pcnp->pcn_flags & PCN_INTR_ENABLED)
541*438b5f69SJason King 		ddi_remove_intr(pcnp->pcn_dip, 0, pcnp->pcn_icookie);
542*438b5f69SJason King 
543*438b5f69SJason King 	/* These will exit gracefully if not yet allocated */
544*438b5f69SJason King 	pcn_freerxring(pcnp);
545*438b5f69SJason King 	pcn_freetxring(pcnp);
546*438b5f69SJason King 
547*438b5f69SJason King 	if (pcnp->pcn_regshandle != NULL)
548*438b5f69SJason King 		ddi_regs_map_free(&pcnp->pcn_regshandle);
549*438b5f69SJason King 
550*438b5f69SJason King 
551*438b5f69SJason King 	mutex_destroy(&pcnp->pcn_xmtlock);
552*438b5f69SJason King 	mutex_destroy(&pcnp->pcn_intrlock);
553*438b5f69SJason King 	mutex_destroy(&pcnp->pcn_reglock);
554*438b5f69SJason King 
555*438b5f69SJason King 	ddi_soft_state_free(pcn_ssp, ddi_get_instance(pcnp->pcn_dip));
556*438b5f69SJason King }
557*438b5f69SJason King 
558*438b5f69SJason King /*
559*438b5f69SJason King  * Drains any FIFOs in the card, then pauses it
560*438b5f69SJason King  */
561*438b5f69SJason King static void
pcn_suspend(pcn_t * pcnp)562*438b5f69SJason King pcn_suspend(pcn_t *pcnp)
563*438b5f69SJason King {
564*438b5f69SJason King 	uint32_t val;
565*438b5f69SJason King 	int i;
566*438b5f69SJason King 
567*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
568*438b5f69SJason King 	for (i = 0; i < 5000; i++) {
569*438b5f69SJason King 		if ((val = pcn_csr_read(pcnp, PCN_CSR_EXTCTL1)) &
570*438b5f69SJason King 		    PCN_EXTCTL1_SPND)
571*438b5f69SJason King 			return;
572*438b5f69SJason King 		drv_usecwait(1000);
573*438b5f69SJason King 	}
574*438b5f69SJason King 
575*438b5f69SJason King 	pcn_error(pcnp->pcn_dip, "Unable to suspend, EXTCTL1 was 0x%b", val,
576*438b5f69SJason King 	    PCN_EXTCTL1_STR);
577*438b5f69SJason King }
578*438b5f69SJason King 
579*438b5f69SJason King static void
pcn_resume(pcn_t * pcnp)580*438b5f69SJason King pcn_resume(pcn_t *pcnp)
581*438b5f69SJason King {
582*438b5f69SJason King 	PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
583*438b5f69SJason King }
584*438b5f69SJason King 
585*438b5f69SJason King static int
pcn_m_multicast(void * arg,boolean_t add,const uint8_t * macaddr)586*438b5f69SJason King pcn_m_multicast(void *arg, boolean_t add, const uint8_t *macaddr)
587*438b5f69SJason King {
588*438b5f69SJason King 	pcn_t		*pcnp = (pcn_t *)arg;
589*438b5f69SJason King 	int		index;
590*438b5f69SJason King 	uint32_t	crc;
591*438b5f69SJason King 	uint16_t	bit;
592*438b5f69SJason King 	uint16_t	newval, oldval;
593*438b5f69SJason King 
594*438b5f69SJason King 	/*
595*438b5f69SJason King 	 * PCNet uses the upper 6 bits of the CRC of the macaddr
596*438b5f69SJason King 	 * to index into a 64bit mask
597*438b5f69SJason King 	 */
598*438b5f69SJason King 	CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
599*438b5f69SJason King 	crc >>= 26;
600*438b5f69SJason King 	index = crc / 16;
601*438b5f69SJason King 	bit = (1U << (crc % 16));
602*438b5f69SJason King 
603*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
604*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
605*438b5f69SJason King 	newval = oldval = pcnp->pcn_mctab[index];
606*438b5f69SJason King 
607*438b5f69SJason King 	if (add) {
608*438b5f69SJason King 		pcnp->pcn_mccount[crc]++;
609*438b5f69SJason King 		if (pcnp->pcn_mccount[crc] == 1)
610*438b5f69SJason King 			newval |= bit;
611*438b5f69SJason King 	} else {
612*438b5f69SJason King 		pcnp->pcn_mccount[crc]--;
613*438b5f69SJason King 		if (pcnp->pcn_mccount[crc] == 0)
614*438b5f69SJason King 			newval &= ~bit;
615*438b5f69SJason King 	}
616*438b5f69SJason King 	if (newval != oldval) {
617*438b5f69SJason King 		pcnp->pcn_mctab[index] = newval;
618*438b5f69SJason King 		pcn_suspend(pcnp);
619*438b5f69SJason King 		pcn_csr_write(pcnp, PCN_CSR_MAR0 + index, newval);
620*438b5f69SJason King 		pcn_resume(pcnp);
621*438b5f69SJason King 	}
622*438b5f69SJason King 
623*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
624*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
625*438b5f69SJason King 
626*438b5f69SJason King 	return (0);
627*438b5f69SJason King }
628*438b5f69SJason King 
629*438b5f69SJason King static int
pcn_m_promisc(void * arg,boolean_t on)630*438b5f69SJason King pcn_m_promisc(void *arg, boolean_t on)
631*438b5f69SJason King {
632*438b5f69SJason King 	pcn_t		*pcnp = (pcn_t *)arg;
633*438b5f69SJason King 
634*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
635*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
636*438b5f69SJason King 
637*438b5f69SJason King 	pcnp->pcn_promisc = on;
638*438b5f69SJason King 
639*438b5f69SJason King 	if (IS_RUNNING(pcnp))
640*438b5f69SJason King 		pcn_suspend(pcnp);
641*438b5f69SJason King 
642*438b5f69SJason King 	/* set promiscuous mode */
643*438b5f69SJason King 	if (pcnp->pcn_promisc)
644*438b5f69SJason King 		PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
645*438b5f69SJason King 	else
646*438b5f69SJason King 		PCN_CSR_CLRBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
647*438b5f69SJason King 
648*438b5f69SJason King 	if (IS_RUNNING(pcnp))
649*438b5f69SJason King 		pcn_resume(pcnp);
650*438b5f69SJason King 
651*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
652*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
653*438b5f69SJason King 
654*438b5f69SJason King 	return (0);
655*438b5f69SJason King }
656*438b5f69SJason King 
657*438b5f69SJason King static int
pcn_m_unicast(void * arg,const uint8_t * macaddr)658*438b5f69SJason King pcn_m_unicast(void *arg, const uint8_t *macaddr)
659*438b5f69SJason King {
660*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
661*438b5f69SJason King 	int i;
662*438b5f69SJason King 	uint16_t addr[3];
663*438b5f69SJason King 
664*438b5f69SJason King 	bcopy(macaddr, addr, sizeof (addr));
665*438b5f69SJason King 
666*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
667*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
668*438b5f69SJason King 
669*438b5f69SJason King 	if (IS_RUNNING(pcnp))
670*438b5f69SJason King 		pcn_suspend(pcnp);
671*438b5f69SJason King 
672*438b5f69SJason King 	for (i = 0; i < 3; i++)
673*438b5f69SJason King 		pcn_csr_write(pcnp, PCN_CSR_PAR0 + i, addr[i]);
674*438b5f69SJason King 
675*438b5f69SJason King 	bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL);
676*438b5f69SJason King 
677*438b5f69SJason King 	if (IS_RUNNING(pcnp))
678*438b5f69SJason King 		pcn_resume(pcnp);
679*438b5f69SJason King 
680*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
681*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
682*438b5f69SJason King 
683*438b5f69SJason King 	return (0);
684*438b5f69SJason King }
685*438b5f69SJason King 
686*438b5f69SJason King static mblk_t *
pcn_m_tx(void * arg,mblk_t * mp)687*438b5f69SJason King pcn_m_tx(void *arg, mblk_t *mp)
688*438b5f69SJason King {
689*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
690*438b5f69SJason King 	mblk_t	*nmp;
691*438b5f69SJason King 
692*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
693*438b5f69SJason King 
694*438b5f69SJason King 	if (pcnp->pcn_flags & PCN_SUSPENDED) {
695*438b5f69SJason King 		while ((nmp = mp) != NULL) {
696*438b5f69SJason King 			pcnp->pcn_carrier_errors++;
697*438b5f69SJason King 			mp = mp->b_next;
698*438b5f69SJason King 			freemsg(nmp);
699*438b5f69SJason King 		}
700*438b5f69SJason King 		mutex_exit(&pcnp->pcn_xmtlock);
701*438b5f69SJason King 		return (NULL);
702*438b5f69SJason King 	}
703*438b5f69SJason King 
704*438b5f69SJason King 	while (mp != NULL) {
705*438b5f69SJason King 		nmp = mp->b_next;
706*438b5f69SJason King 		mp->b_next = NULL;
707*438b5f69SJason King 
708*438b5f69SJason King 		if (!pcn_send(pcnp, mp)) {
709*438b5f69SJason King 			mp->b_next = nmp;
710*438b5f69SJason King 			break;
711*438b5f69SJason King 		}
712*438b5f69SJason King 		mp = nmp;
713*438b5f69SJason King 	}
714*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
715*438b5f69SJason King 
716*438b5f69SJason King 	return (mp);
717*438b5f69SJason King }
718*438b5f69SJason King 
719*438b5f69SJason King static boolean_t
pcn_send(pcn_t * pcnp,mblk_t * mp)720*438b5f69SJason King pcn_send(pcn_t *pcnp, mblk_t *mp)
721*438b5f69SJason King {
722*438b5f69SJason King 	size_t		len;
723*438b5f69SJason King 	pcn_buf_t	*txb;
724*438b5f69SJason King 	pcn_tx_desc_t	*tmd;
725*438b5f69SJason King 	int		txsend;
726*438b5f69SJason King 
727*438b5f69SJason King 	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
728*438b5f69SJason King 	ASSERT(mp != NULL);
729*438b5f69SJason King 
730*438b5f69SJason King 	len = msgsize(mp);
731*438b5f69SJason King 	if (len > ETHERVLANMTU) {
732*438b5f69SJason King 		pcnp->pcn_macxmt_errors++;
733*438b5f69SJason King 		freemsg(mp);
734*438b5f69SJason King 		return (B_TRUE);
735*438b5f69SJason King 	}
736*438b5f69SJason King 
737*438b5f69SJason King 	if (pcnp->pcn_txavail < PCN_TXRECLAIM)
738*438b5f69SJason King 		pcn_reclaim(pcnp);
739*438b5f69SJason King 
740*438b5f69SJason King 	if (pcnp->pcn_txavail == 0) {
741*438b5f69SJason King 		pcnp->pcn_wantw = B_TRUE;
742*438b5f69SJason King 
743*438b5f69SJason King 		/* enable tx interrupt */
744*438b5f69SJason King 		PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_LTINTEN);
745*438b5f69SJason King 		return (B_FALSE);
746*438b5f69SJason King 	}
747*438b5f69SJason King 
748*438b5f69SJason King 	txsend = pcnp->pcn_txsend;
749*438b5f69SJason King 
750*438b5f69SJason King 	/*
751*438b5f69SJason King 	 * We copy the packet to a single buffer.  NetBSD sources suggest
752*438b5f69SJason King 	 * that if multiple segements are ever used, VMware has a bug that will
753*438b5f69SJason King 	 * only allow 8 segments to be used, while the physical chips allow 16
754*438b5f69SJason King 	 */
755*438b5f69SJason King 	txb = pcnp->pcn_txbufs[txsend];
756*438b5f69SJason King 	mcopymsg(mp, txb->pb_buf);	/* frees mp! */
757*438b5f69SJason King 
758*438b5f69SJason King 	pcnp->pcn_opackets++;
759*438b5f69SJason King 	pcnp->pcn_obytes += len;
760*438b5f69SJason King 	if (txb->pb_buf[0] & 0x1) {
761*438b5f69SJason King 		if (bcmp(txb->pb_buf, pcn_broadcast, ETHERADDRL) != 0)
762*438b5f69SJason King 			pcnp->pcn_multixmt++;
763*438b5f69SJason King 		else
764*438b5f69SJason King 			pcnp->pcn_brdcstxmt++;
765*438b5f69SJason King 	}
766*438b5f69SJason King 
767*438b5f69SJason King 	tmd = &pcnp->pcn_txdescp[txsend];
768*438b5f69SJason King 
769*438b5f69SJason King 	SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV);
770*438b5f69SJason King 	tmd->pcn_txstat = 0;
771*438b5f69SJason King 	tmd->pcn_tbaddr = txb->pb_paddr;
772*438b5f69SJason King 
773*438b5f69SJason King 	/* PCNet wants the 2's complement of the length of the buffer */
774*438b5f69SJason King 	tmd->pcn_txctl = (~(len) + 1) & PCN_TXCTL_BUFSZ;
775*438b5f69SJason King 	tmd->pcn_txctl |= PCN_TXCTL_MBO;
776*438b5f69SJason King 	tmd->pcn_txctl |= PCN_TXCTL_STP | PCN_TXCTL_ENP | PCN_TXCTL_ADD_FCS |
777*438b5f69SJason King 	    PCN_TXCTL_OWN | PCN_TXCTL_MORE_LTINT;
778*438b5f69SJason King 
779*438b5f69SJason King 	SYNCTXDESC(pcnp, txsend, DDI_DMA_SYNC_FORDEV);
780*438b5f69SJason King 
781*438b5f69SJason King 	pcnp->pcn_txavail--;
782*438b5f69SJason King 	pcnp->pcn_txsend = (txsend + 1) % PCN_TXRING;
783*438b5f69SJason King 	pcnp->pcn_txstall_time = gethrtime() + (5 * 1000000000ULL);
784*438b5f69SJason King 
785*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);
786*438b5f69SJason King 
787*438b5f69SJason King 	return (B_TRUE);
788*438b5f69SJason King }
789*438b5f69SJason King 
790*438b5f69SJason King static void
pcn_reclaim(pcn_t * pcnp)791*438b5f69SJason King pcn_reclaim(pcn_t *pcnp)
792*438b5f69SJason King {
793*438b5f69SJason King 	pcn_tx_desc_t	*tmdp;
794*438b5f69SJason King 
795*438b5f69SJason King 	while (pcnp->pcn_txavail != PCN_TXRING) {
796*438b5f69SJason King 		int index = pcnp->pcn_txreclaim;
797*438b5f69SJason King 
798*438b5f69SJason King 		tmdp = &pcnp->pcn_txdescp[index];
799*438b5f69SJason King 
800*438b5f69SJason King 		/* sync before reading */
801*438b5f69SJason King 		SYNCTXDESC(pcnp, index, DDI_DMA_SYNC_FORKERNEL);
802*438b5f69SJason King 
803*438b5f69SJason King 		/* check if chip is still working on it */
804*438b5f69SJason King 		if (tmdp->pcn_txctl & PCN_TXCTL_OWN)
805*438b5f69SJason King 			break;
806*438b5f69SJason King 
807*438b5f69SJason King 		pcnp->pcn_txavail++;
808*438b5f69SJason King 		pcnp->pcn_txreclaim = (index + 1) % PCN_TXRING;
809*438b5f69SJason King 	}
810*438b5f69SJason King 
811*438b5f69SJason King 	if (pcnp->pcn_txavail >= PCN_TXRESCHED) {
812*438b5f69SJason King 		if (pcnp->pcn_wantw) {
813*438b5f69SJason King 			pcnp->pcn_wantw = B_FALSE;
814*438b5f69SJason King 
815*438b5f69SJason King 			/* Disable TX interrupt */
816*438b5f69SJason King 			PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1,
817*438b5f69SJason King 			    PCN_EXTCTL1_LTINTEN);
818*438b5f69SJason King 
819*438b5f69SJason King 			mac_tx_update(pcnp->pcn_mh);
820*438b5f69SJason King 		}
821*438b5f69SJason King 	}
822*438b5f69SJason King }
823*438b5f69SJason King 
824*438b5f69SJason King static unsigned
pcn_intr(caddr_t arg1)825*438b5f69SJason King pcn_intr(caddr_t arg1)
826*438b5f69SJason King {
827*438b5f69SJason King 	pcn_t		*pcnp = (void *)arg1;
828*438b5f69SJason King 	mblk_t		*mp = NULL;
829*438b5f69SJason King 	uint32_t	status, status2;
830*438b5f69SJason King 	boolean_t	do_reset = B_FALSE;
831*438b5f69SJason King 
832*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
833*438b5f69SJason King 
834*438b5f69SJason King 	if (IS_SUSPENDED(pcnp)) {
835*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
836*438b5f69SJason King 		return (DDI_INTR_UNCLAIMED);
837*438b5f69SJason King 	}
838*438b5f69SJason King 
839*438b5f69SJason King 	while ((status = pcn_csr_read(pcnp, PCN_CSR_CSR)) & PCN_CSR_INTR) {
840*438b5f69SJason King 		pcn_csr_write(pcnp, PCN_CSR_CSR, status);
841*438b5f69SJason King 
842*438b5f69SJason King 		status2 = pcn_csr_read(pcnp, PCN_CSR_EXTCTL2);
843*438b5f69SJason King 
844*438b5f69SJason King 		if (status & PCN_CSR_TINT) {
845*438b5f69SJason King 			mutex_enter(&pcnp->pcn_xmtlock);
846*438b5f69SJason King 			pcn_reclaim(pcnp);
847*438b5f69SJason King 			mutex_exit(&pcnp->pcn_xmtlock);
848*438b5f69SJason King 		}
849*438b5f69SJason King 
850*438b5f69SJason King 		if (status & PCN_CSR_RINT)
851*438b5f69SJason King 			mp = pcn_receive(pcnp);
852*438b5f69SJason King 
853*438b5f69SJason King 		if (status & PCN_CSR_ERR) {
854*438b5f69SJason King 			do_reset = B_TRUE;
855*438b5f69SJason King 			break;
856*438b5f69SJason King 		}
857*438b5f69SJason King 
858*438b5f69SJason King 		/* timer interrupt */
859*438b5f69SJason King 		if (status2 & PCN_EXTCTL2_STINT) {
860*438b5f69SJason King 			/* ack it */
861*438b5f69SJason King 			PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2,
862*438b5f69SJason King 			    PCN_EXTCTL2_STINT);
863*438b5f69SJason King 
864*438b5f69SJason King 			if (pcn_watchdog(pcnp) != DDI_SUCCESS) {
865*438b5f69SJason King 				do_reset = B_TRUE;
866*438b5f69SJason King 				break;
867*438b5f69SJason King 			}
868*438b5f69SJason King 		}
869*438b5f69SJason King 	}
870*438b5f69SJason King 
871*438b5f69SJason King 	if (do_reset) {
872*438b5f69SJason King 		mutex_enter(&pcnp->pcn_xmtlock);
873*438b5f69SJason King 		pcn_resetall(pcnp);
874*438b5f69SJason King 		mutex_exit(&pcnp->pcn_xmtlock);
875*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
876*438b5f69SJason King 
877*438b5f69SJason King 		mii_reset(pcnp->pcn_mii);
878*438b5f69SJason King 	} else {
879*438b5f69SJason King 		mutex_exit(&pcnp->pcn_intrlock);
880*438b5f69SJason King 	}
881*438b5f69SJason King 
882*438b5f69SJason King 	if (mp)
883*438b5f69SJason King 		mac_rx(pcnp->pcn_mh, NULL, mp);
884*438b5f69SJason King 
885*438b5f69SJason King 	return (DDI_INTR_CLAIMED);
886*438b5f69SJason King }
887*438b5f69SJason King 
888*438b5f69SJason King static mblk_t *
pcn_receive(pcn_t * pcnp)889*438b5f69SJason King pcn_receive(pcn_t *pcnp)
890*438b5f69SJason King {
891*438b5f69SJason King 	uint32_t	len;
892*438b5f69SJason King 	pcn_buf_t	*rxb;
893*438b5f69SJason King 	pcn_rx_desc_t	*rmd;
894*438b5f69SJason King 	mblk_t		*mpchain, **mpp, *mp;
895*438b5f69SJason King 	int		head, cnt;
896*438b5f69SJason King 
897*438b5f69SJason King 	mpchain = NULL;
898*438b5f69SJason King 	mpp = &mpchain;
899*438b5f69SJason King 	head = pcnp->pcn_rxhead;
900*438b5f69SJason King 
901*438b5f69SJason King 	for (cnt = 0; cnt < PCN_RXRING; cnt++) {
902*438b5f69SJason King 		rmd = &pcnp->pcn_rxdescp[head];
903*438b5f69SJason King 		rxb = pcnp->pcn_rxbufs[head];
904*438b5f69SJason King 
905*438b5f69SJason King 		SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORKERNEL);
906*438b5f69SJason King 		if (rmd->pcn_rxstat & PCN_RXSTAT_OWN)
907*438b5f69SJason King 			break;
908*438b5f69SJason King 
909*438b5f69SJason King 		len = rmd->pcn_rxlen - ETHERFCSL;
910*438b5f69SJason King 
911*438b5f69SJason King 		if (rmd->pcn_rxstat & PCN_RXSTAT_ERR) {
912*438b5f69SJason King 			pcnp->pcn_errrcv++;
913*438b5f69SJason King 
914*438b5f69SJason King 			if (rmd->pcn_rxstat & PCN_RXSTAT_FRAM)
915*438b5f69SJason King 				pcnp->pcn_align_errors++;
916*438b5f69SJason King 			if (rmd->pcn_rxstat & PCN_RXSTAT_OFLOW)
917*438b5f69SJason King 				pcnp->pcn_overflow++;
918*438b5f69SJason King 			if (rmd->pcn_rxstat & PCN_RXSTAT_CRC)
919*438b5f69SJason King 				pcnp->pcn_fcs_errors++;
920*438b5f69SJason King 		} else if (len > ETHERVLANMTU) {
921*438b5f69SJason King 			pcnp->pcn_errrcv++;
922*438b5f69SJason King 			pcnp->pcn_toolong_errors++;
923*438b5f69SJason King 		} else {
924*438b5f69SJason King 			mp = allocb(len + PCN_HEADROOM, 0);
925*438b5f69SJason King 			if (mp == NULL) {
926*438b5f69SJason King 				pcnp->pcn_errrcv++;
927*438b5f69SJason King 				pcnp->pcn_norcvbuf++;
928*438b5f69SJason King 				goto skip;
929*438b5f69SJason King 			}
930*438b5f69SJason King 
931*438b5f69SJason King 			SYNCBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
932*438b5f69SJason King 			mp->b_rptr += PCN_HEADROOM;
933*438b5f69SJason King 			mp->b_wptr = mp->b_rptr + len;
934*438b5f69SJason King 			bcopy((char *)rxb->pb_buf, mp->b_rptr, len);
935*438b5f69SJason King 
936*438b5f69SJason King 			pcnp->pcn_ipackets++;
937*438b5f69SJason King 			pcnp->pcn_rbytes++;
938*438b5f69SJason King 
939*438b5f69SJason King 			if (rmd->pcn_rxstat & PCN_RXSTAT_LAFM|PCN_RXSTAT_BAM) {
940*438b5f69SJason King 				if (rmd->pcn_rxstat & PCN_RXSTAT_BAM)
941*438b5f69SJason King 					pcnp->pcn_brdcstrcv++;
942*438b5f69SJason King 				else
943*438b5f69SJason King 					pcnp->pcn_multircv++;
944*438b5f69SJason King 			}
945*438b5f69SJason King 			*mpp = mp;
946*438b5f69SJason King 			mpp = &mp->b_next;
947*438b5f69SJason King 		}
948*438b5f69SJason King 
949*438b5f69SJason King skip:
950*438b5f69SJason King 		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
951*438b5f69SJason King 		SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORDEV);
952*438b5f69SJason King 
953*438b5f69SJason King 		head = (head + 1) % PCN_RXRING;
954*438b5f69SJason King 	}
955*438b5f69SJason King 
956*438b5f69SJason King 	pcnp->pcn_rxhead = head;
957*438b5f69SJason King 	return (mpchain);
958*438b5f69SJason King }
959*438b5f69SJason King 
960*438b5f69SJason King static void
pcn_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)961*438b5f69SJason King pcn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
962*438b5f69SJason King {
963*438b5f69SJason King 	pcn_t *pcnp = (pcn_t *)arg;
964*438b5f69SJason King 
965*438b5f69SJason King 	if (mii_m_loop_ioctl(pcnp->pcn_mii, wq, mp))
966*438b5f69SJason King 		return;
967*438b5f69SJason King 
968*438b5f69SJason King 	miocnak(wq, mp, 0, EINVAL);
969*438b5f69SJason King }
970*438b5f69SJason King 
971*438b5f69SJason King static int
pcn_m_start(void * arg)972*438b5f69SJason King pcn_m_start(void *arg)
973*438b5f69SJason King {
974*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
975*438b5f69SJason King 
976*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
977*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
978*438b5f69SJason King 
979*438b5f69SJason King 	pcn_startall(pcnp);
980*438b5f69SJason King 	pcnp->pcn_flags |= PCN_RUNNING;
981*438b5f69SJason King 
982*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
983*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
984*438b5f69SJason King 
985*438b5f69SJason King 	mii_start(pcnp->pcn_mii);
986*438b5f69SJason King 
987*438b5f69SJason King 	return (0);
988*438b5f69SJason King }
989*438b5f69SJason King 
990*438b5f69SJason King static void
pcn_m_stop(void * arg)991*438b5f69SJason King pcn_m_stop(void *arg)
992*438b5f69SJason King {
993*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
994*438b5f69SJason King 
995*438b5f69SJason King 	mii_stop(pcnp->pcn_mii);
996*438b5f69SJason King 
997*438b5f69SJason King 	mutex_enter(&pcnp->pcn_intrlock);
998*438b5f69SJason King 	mutex_enter(&pcnp->pcn_xmtlock);
999*438b5f69SJason King 
1000*438b5f69SJason King 	pcn_stopall(pcnp);
1001*438b5f69SJason King 	pcnp->pcn_flags &= ~PCN_RUNNING;
1002*438b5f69SJason King 
1003*438b5f69SJason King 	mutex_exit(&pcnp->pcn_xmtlock);
1004*438b5f69SJason King 	mutex_exit(&pcnp->pcn_intrlock);
1005*438b5f69SJason King }
1006*438b5f69SJason King 
1007*438b5f69SJason King static int
pcn_initialize(pcn_t * pcnp,boolean_t getfact)1008*438b5f69SJason King pcn_initialize(pcn_t *pcnp, boolean_t getfact)
1009*438b5f69SJason King {
1010*438b5f69SJason King 	int i;
1011*438b5f69SJason King 	uint16_t addr[3];
1012*438b5f69SJason King 
1013*438b5f69SJason King 	bcopy(pcnp->pcn_addr, addr, sizeof (addr));
1014*438b5f69SJason King 
1015*438b5f69SJason King 	/*
1016*438b5f69SJason King 	 * Issue a reset by reading from the RESET register.
1017*438b5f69SJason King 	 * Note that we don't know if the chip is operating in
1018*438b5f69SJason King 	 * 16-bit or 32-bit mode at this point, so we attempt
1019*438b5f69SJason King 	 * to reset the chip both ways.  If one fails, the other
1020*438b5f69SJason King 	 * will succeed.
1021*438b5f69SJason King 	 */
1022*438b5f69SJason King 	(void) CSR_READ_2(pcnp, PCN_IO16_RESET);
1023*438b5f69SJason King 	(void) CSR_READ_4(pcnp, PCN_IO32_RESET);
1024*438b5f69SJason King 
1025*438b5f69SJason King 	drv_usecwait(1000);
1026*438b5f69SJason King 
1027*438b5f69SJason King 	/* Select 32-bit (DWIO) mode */
1028*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0);
1029*438b5f69SJason King 
1030*438b5f69SJason King 	/* The timer is not affected by a reset, so explicitly disable */
1031*438b5f69SJason King 	pcn_stop_timer(pcnp);
1032*438b5f69SJason King 
1033*438b5f69SJason King 	/* Enable fast suspend */
1034*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);
1035*438b5f69SJason King 
1036*438b5f69SJason King 	/* Select Style 3 descriptors */
1037*438b5f69SJason King 	pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI);
1038*438b5f69SJason King 
1039*438b5f69SJason King 	/* Set MAC address */
1040*438b5f69SJason King 	if (getfact)
1041*438b5f69SJason King 		pcn_getfactaddr(pcnp);
1042*438b5f69SJason King 
1043*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_PAR0, addr[0]);
1044*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_PAR1, addr[1]);
1045*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_PAR2, addr[2]);
1046*438b5f69SJason King 
1047*438b5f69SJason King 	/* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */
1048*438b5f69SJason King 	PCN_BCR_CLRBIT(pcnp, PCN_BCR_MISCCFG, PCN_MISC_ASEL);
1049*438b5f69SJason King 
1050*438b5f69SJason King 	/*
1051*438b5f69SJason King 	 * XXX: need to find a way to determine when 10bt media is
1052*438b5f69SJason King 	 * selected for non Am79C978, and set to PCN_PORT_10BASET
1053*438b5f69SJason King 	 * instead of PCN_PORT_MII
1054*438b5f69SJason King 	 */
1055*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_MODE, PCN_PORT_MII);
1056*438b5f69SJason King 
1057*438b5f69SJason King 	/* Reenable auto negotiation for external phy */
1058*438b5f69SJason King 	PCN_BCR_SETBIT(pcnp, PCN_BCR_MIICTL, PCN_MIICTL_XPHYANE);
1059*438b5f69SJason King 
1060*438b5f69SJason King 	if (pcnp->pcn_promisc)
1061*438b5f69SJason King 		PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
1062*438b5f69SJason King 
1063*438b5f69SJason King 	/* Initalize mcast addr filter */
1064*438b5f69SJason King 	for (i = 0; i < 4; i++)
1065*438b5f69SJason King 		pcn_csr_write(pcnp, PCN_CSR_MAR0 + i, pcnp->pcn_mctab[i]);
1066*438b5f69SJason King 
1067*438b5f69SJason King 	pcn_resetrings(pcnp);
1068*438b5f69SJason King 
1069*438b5f69SJason King 	/* We're not using the initialization block. */
1070*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_IAB1, 0);
1071*438b5f69SJason King 
1072*438b5f69SJason King 	/*
1073*438b5f69SJason King 	 * Enable burst read and write.  Also set the no underflow
1074*438b5f69SJason King 	 * bit.  This will avoid transmit underruns in ceratin
1075*438b5f69SJason King 	 * conditions while still providing decent performance.
1076*438b5f69SJason King 	 */
1077*438b5f69SJason King 	PCN_BCR_SETBIT(pcnp, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW |
1078*438b5f69SJason King 	    PCN_BUSCTL_BREAD | PCN_BUSCTL_BWRITE);
1079*438b5f69SJason King 
1080*438b5f69SJason King 	/* Enable graceful recovery from underflow. */
1081*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_IMR, PCN_IMR_DXSUFLO);
1082*438b5f69SJason King 
1083*438b5f69SJason King 	/* Enable auto-padding of short TX frames. */
1084*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX);
1085*438b5f69SJason King 
1086*438b5f69SJason King 	if (pcnp->pcn_type == Am79C978)
1087*438b5f69SJason King 		pcn_bcr_write(pcnp, PCN_BCR_PHYSEL,
1088*438b5f69SJason King 		    PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA);
1089*438b5f69SJason King 
1090*438b5f69SJason King 	return (DDI_SUCCESS);
1091*438b5f69SJason King }
1092*438b5f69SJason King 
1093*438b5f69SJason King static void
pcn_resetall(pcn_t * pcnp)1094*438b5f69SJason King pcn_resetall(pcn_t *pcnp)
1095*438b5f69SJason King {
1096*438b5f69SJason King 	pcn_stopall(pcnp);
1097*438b5f69SJason King 	pcn_startall(pcnp);
1098*438b5f69SJason King }
1099*438b5f69SJason King 
1100*438b5f69SJason King static void
pcn_startall(pcn_t * pcnp)1101*438b5f69SJason King pcn_startall(pcn_t *pcnp)
1102*438b5f69SJason King {
1103*438b5f69SJason King 	ASSERT(mutex_owned(&pcnp->pcn_intrlock));
1104*438b5f69SJason King 	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
1105*438b5f69SJason King 
1106*438b5f69SJason King 	(void) pcn_initialize(pcnp, B_FALSE);
1107*438b5f69SJason King 
1108*438b5f69SJason King 	/* Start chip and enable interrupts */
1109*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_START|PCN_CSR_INTEN);
1110*438b5f69SJason King 
1111*438b5f69SJason King 	pcn_start_timer(pcnp);
1112*438b5f69SJason King 
1113*438b5f69SJason King 	if (IS_RUNNING(pcnp))
1114*438b5f69SJason King 		mac_tx_update(pcnp->pcn_mh);
1115*438b5f69SJason King }
1116*438b5f69SJason King 
1117*438b5f69SJason King static void
pcn_stopall(pcn_t * pcnp)1118*438b5f69SJason King pcn_stopall(pcn_t *pcnp)
1119*438b5f69SJason King {
1120*438b5f69SJason King 	ASSERT(mutex_owned(&pcnp->pcn_intrlock));
1121*438b5f69SJason King 	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
1122*438b5f69SJason King 
1123*438b5f69SJason King 	pcn_stop_timer(pcnp);
1124*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP);
1125*438b5f69SJason King }
1126*438b5f69SJason King 
1127*438b5f69SJason King /*
1128*438b5f69SJason King  * The soft timer is not affected by a soft reset (according to the datasheet)
1129*438b5f69SJason King  * so it must always be explicitly enabled and disabled
1130*438b5f69SJason King  */
1131*438b5f69SJason King static void
pcn_start_timer(pcn_t * pcnp)1132*438b5f69SJason King pcn_start_timer(pcn_t *pcnp)
1133*438b5f69SJason King {
1134*438b5f69SJason King 	PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
1135*438b5f69SJason King 
1136*438b5f69SJason King 	/*
1137*438b5f69SJason King 	 * The frequency this fires varies based on the particular
1138*438b5f69SJason King 	 * model, this value is largely arbitrary. It just needs to
1139*438b5f69SJason King 	 * fire often enough to detect a stall
1140*438b5f69SJason King 	 */
1141*438b5f69SJason King 	pcn_bcr_write(pcnp, PCN_BCR_TIMER, 0xa000);
1142*438b5f69SJason King }
1143*438b5f69SJason King 
1144*438b5f69SJason King 
1145*438b5f69SJason King static void
pcn_stop_timer(pcn_t * pcnp)1146*438b5f69SJason King pcn_stop_timer(pcn_t *pcnp)
1147*438b5f69SJason King {
1148*438b5f69SJason King 	PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
1149*438b5f69SJason King }
1150*438b5f69SJason King 
1151*438b5f69SJason King static int
pcn_m_stat(void * arg,uint_t stat,uint64_t * val)1152*438b5f69SJason King pcn_m_stat(void *arg, uint_t stat, uint64_t *val)
1153*438b5f69SJason King {
1154*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
1155*438b5f69SJason King 
1156*438b5f69SJason King 	if (mii_m_getstat(pcnp->pcn_mii, stat, val) == 0)
1157*438b5f69SJason King 		return (0);
1158*438b5f69SJason King 
1159*438b5f69SJason King 	switch (stat) {
1160*438b5f69SJason King 	case MAC_STAT_MULTIRCV:
1161*438b5f69SJason King 		*val = pcnp->pcn_multircv;
1162*438b5f69SJason King 		break;
1163*438b5f69SJason King 
1164*438b5f69SJason King 	case MAC_STAT_BRDCSTRCV:
1165*438b5f69SJason King 		*val = pcnp->pcn_brdcstrcv;
1166*438b5f69SJason King 		break;
1167*438b5f69SJason King 
1168*438b5f69SJason King 	case MAC_STAT_MULTIXMT:
1169*438b5f69SJason King 		*val = pcnp->pcn_multixmt;
1170*438b5f69SJason King 		break;
1171*438b5f69SJason King 
1172*438b5f69SJason King 	case MAC_STAT_BRDCSTXMT:
1173*438b5f69SJason King 		*val = pcnp->pcn_brdcstxmt;
1174*438b5f69SJason King 		break;
1175*438b5f69SJason King 
1176*438b5f69SJason King 	case MAC_STAT_IPACKETS:
1177*438b5f69SJason King 		*val = pcnp->pcn_ipackets;
1178*438b5f69SJason King 		break;
1179*438b5f69SJason King 
1180*438b5f69SJason King 	case MAC_STAT_RBYTES:
1181*438b5f69SJason King 		*val = pcnp->pcn_rbytes;
1182*438b5f69SJason King 		break;
1183*438b5f69SJason King 
1184*438b5f69SJason King 	case MAC_STAT_OPACKETS:
1185*438b5f69SJason King 		*val = pcnp->pcn_opackets;
1186*438b5f69SJason King 		break;
1187*438b5f69SJason King 
1188*438b5f69SJason King 	case MAC_STAT_OBYTES:
1189*438b5f69SJason King 		*val = pcnp->pcn_obytes;
1190*438b5f69SJason King 		break;
1191*438b5f69SJason King 
1192*438b5f69SJason King 	case MAC_STAT_NORCVBUF:
1193*438b5f69SJason King 		*val = pcnp->pcn_norcvbuf;
1194*438b5f69SJason King 		break;
1195*438b5f69SJason King 
1196*438b5f69SJason King 	case MAC_STAT_NOXMTBUF:
1197*438b5f69SJason King 		*val = 0;
1198*438b5f69SJason King 		break;
1199*438b5f69SJason King 
1200*438b5f69SJason King 	case MAC_STAT_COLLISIONS:
1201*438b5f69SJason King 		*val = pcnp->pcn_collisions;
1202*438b5f69SJason King 		break;
1203*438b5f69SJason King 
1204*438b5f69SJason King 	case MAC_STAT_IERRORS:
1205*438b5f69SJason King 		*val = pcnp->pcn_errrcv;
1206*438b5f69SJason King 		break;
1207*438b5f69SJason King 
1208*438b5f69SJason King 	case MAC_STAT_OERRORS:
1209*438b5f69SJason King 		*val = pcnp->pcn_errxmt;
1210*438b5f69SJason King 		break;
1211*438b5f69SJason King 
1212*438b5f69SJason King 	case ETHER_STAT_ALIGN_ERRORS:
1213*438b5f69SJason King 		*val = pcnp->pcn_align_errors;
1214*438b5f69SJason King 		break;
1215*438b5f69SJason King 
1216*438b5f69SJason King 	case ETHER_STAT_FCS_ERRORS:
1217*438b5f69SJason King 		*val = pcnp->pcn_fcs_errors;
1218*438b5f69SJason King 		break;
1219*438b5f69SJason King 
1220*438b5f69SJason King 	case ETHER_STAT_SQE_ERRORS:
1221*438b5f69SJason King 		*val = pcnp->pcn_sqe_errors;
1222*438b5f69SJason King 		break;
1223*438b5f69SJason King 
1224*438b5f69SJason King 	case ETHER_STAT_DEFER_XMTS:
1225*438b5f69SJason King 		*val = pcnp->pcn_defer_xmts;
1226*438b5f69SJason King 		break;
1227*438b5f69SJason King 
1228*438b5f69SJason King 	case ETHER_STAT_FIRST_COLLISIONS:
1229*438b5f69SJason King 		*val = pcnp->pcn_first_collisions;
1230*438b5f69SJason King 		break;
1231*438b5f69SJason King 
1232*438b5f69SJason King 	case ETHER_STAT_MULTI_COLLISIONS:
1233*438b5f69SJason King 		*val = pcnp->pcn_multi_collisions;
1234*438b5f69SJason King 		break;
1235*438b5f69SJason King 
1236*438b5f69SJason King 	case ETHER_STAT_TX_LATE_COLLISIONS:
1237*438b5f69SJason King 		*val = pcnp->pcn_tx_late_collisions;
1238*438b5f69SJason King 		break;
1239*438b5f69SJason King 
1240*438b5f69SJason King 	case ETHER_STAT_EX_COLLISIONS:
1241*438b5f69SJason King 		*val = pcnp->pcn_ex_collisions;
1242*438b5f69SJason King 		break;
1243*438b5f69SJason King 
1244*438b5f69SJason King 	case ETHER_STAT_MACXMT_ERRORS:
1245*438b5f69SJason King 		*val = pcnp->pcn_macxmt_errors;
1246*438b5f69SJason King 		break;
1247*438b5f69SJason King 
1248*438b5f69SJason King 	case ETHER_STAT_CARRIER_ERRORS:
1249*438b5f69SJason King 		*val = pcnp->pcn_carrier_errors;
1250*438b5f69SJason King 		break;
1251*438b5f69SJason King 
1252*438b5f69SJason King 	case ETHER_STAT_TOOLONG_ERRORS:
1253*438b5f69SJason King 		*val = pcnp->pcn_toolong_errors;
1254*438b5f69SJason King 		break;
1255*438b5f69SJason King 
1256*438b5f69SJason King 	case ETHER_STAT_MACRCV_ERRORS:
1257*438b5f69SJason King 		*val = pcnp->pcn_macrcv_errors;
1258*438b5f69SJason King 		break;
1259*438b5f69SJason King 
1260*438b5f69SJason King 	case MAC_STAT_OVERFLOWS:
1261*438b5f69SJason King 		*val = pcnp->pcn_overflow;
1262*438b5f69SJason King 		break;
1263*438b5f69SJason King 
1264*438b5f69SJason King 	case MAC_STAT_UNDERFLOWS:
1265*438b5f69SJason King 		*val = pcnp->pcn_underflow;
1266*438b5f69SJason King 		break;
1267*438b5f69SJason King 
1268*438b5f69SJason King 	case ETHER_STAT_TOOSHORT_ERRORS:
1269*438b5f69SJason King 		*val = pcnp->pcn_runt;
1270*438b5f69SJason King 		break;
1271*438b5f69SJason King 
1272*438b5f69SJason King 	case ETHER_STAT_JABBER_ERRORS:
1273*438b5f69SJason King 		*val = pcnp->pcn_jabber;
1274*438b5f69SJason King 		break;
1275*438b5f69SJason King 
1276*438b5f69SJason King 	default:
1277*438b5f69SJason King 		return (ENOTSUP);
1278*438b5f69SJason King 	}
1279*438b5f69SJason King 	return (0);
1280*438b5f69SJason King }
1281*438b5f69SJason King 
1282*438b5f69SJason King static int
pcn_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)1283*438b5f69SJason King pcn_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
1284*438b5f69SJason King     void *val)
1285*438b5f69SJason King {
1286*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
1287*438b5f69SJason King 
1288*438b5f69SJason King 	return (mii_m_getprop(pcnp->pcn_mii, name, num, sz, val));
1289*438b5f69SJason King }
1290*438b5f69SJason King 
1291*438b5f69SJason King static int
pcn_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)1292*438b5f69SJason King pcn_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
1293*438b5f69SJason King     const void *val)
1294*438b5f69SJason King {
1295*438b5f69SJason King 	pcn_t	*pcnp = (pcn_t *)arg;
1296*438b5f69SJason King 
1297*438b5f69SJason King 	return (mii_m_setprop(pcnp->pcn_mii, name, num, sz, val));
1298*438b5f69SJason King }
1299*438b5f69SJason King 
1300*438b5f69SJason King static void
pcn_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t prh)1301*438b5f69SJason King pcn_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
1302*438b5f69SJason King     mac_prop_info_handle_t prh)
1303*438b5f69SJason King {
1304*438b5f69SJason King 	pcn_t	*pcnp = arg;
1305*438b5f69SJason King 
1306*438b5f69SJason King 	mii_m_propinfo(pcnp->pcn_mii, name, num, prh);
1307*438b5f69SJason King }
1308*438b5f69SJason King 
1309*438b5f69SJason King static int
pcn_watchdog(pcn_t * pcnp)1310*438b5f69SJason King pcn_watchdog(pcn_t *pcnp)
1311*438b5f69SJason King {
1312*438b5f69SJason King 	if ((pcnp->pcn_txstall_time != 0) &&
1313*438b5f69SJason King 	    (gethrtime() > pcnp->pcn_txstall_time) &&
1314*438b5f69SJason King 	    (pcnp->pcn_txavail != PCN_TXRING)) {
1315*438b5f69SJason King 		pcnp->pcn_txstall_time = 0;
1316*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "TX stall detected!");
1317*438b5f69SJason King 		return (DDI_FAILURE);
1318*438b5f69SJason King 	} else {
1319*438b5f69SJason King 		return (DDI_SUCCESS);
1320*438b5f69SJason King 	}
1321*438b5f69SJason King }
1322*438b5f69SJason King 
1323*438b5f69SJason King static uint16_t
pcn_mii_read(void * arg,uint8_t phy,uint8_t reg)1324*438b5f69SJason King pcn_mii_read(void *arg, uint8_t phy, uint8_t reg)
1325*438b5f69SJason King {
1326*438b5f69SJason King 	pcn_t		*pcnp = (pcn_t *)arg;
1327*438b5f69SJason King 	uint16_t	val;
1328*438b5f69SJason King 
1329*438b5f69SJason King 	/*
1330*438b5f69SJason King 	 * At least Am79C971 with DP83840A wedge when isolating the
1331*438b5f69SJason King 	 * external PHY so we can't allow multiple external PHYs.
1332*438b5f69SJason King 	 * There are cards that use Am79C971 with both the internal
1333*438b5f69SJason King 	 * and an external PHY though.
1334*438b5f69SJason King 	 * For internal PHYs it doesn't really matter whether we can
1335*438b5f69SJason King 	 * isolate the remaining internal and the external ones in
1336*438b5f69SJason King 	 * the PHY drivers as the internal PHYs have to be enabled
1337*438b5f69SJason King 	 * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc.
1338*438b5f69SJason King 	 * With Am79C97{3,5,8} we don't support switching beetween
1339*438b5f69SJason King 	 * the internal and external PHYs, yet, so we can't allow
1340*438b5f69SJason King 	 * multiple PHYs with these either.
1341*438b5f69SJason King 	 * Am79C97{2,6} actually only support external PHYs (not
1342*438b5f69SJason King 	 * connectable internal ones respond at the usual addresses,
1343*438b5f69SJason King 	 * which don't hurt if we let them show up on the bus) and
1344*438b5f69SJason King 	 * isolating them works.
1345*438b5f69SJason King 	 */
1346*438b5f69SJason King 	if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
1347*438b5f69SJason King 	    pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
1348*438b5f69SJason King 	    pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr != -1 &&
1349*438b5f69SJason King 	    phy != pcnp->pcn_extphyaddr) {
1350*438b5f69SJason King 		return (0);
1351*438b5f69SJason King 	}
1352*438b5f69SJason King 
1353*438b5f69SJason King 	val = ((uint16_t)phy << 5) | reg;
1354*438b5f69SJason King 	pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, phy << 5 | reg);
1355*438b5f69SJason King 	val = pcn_bcr_read(pcnp, PCN_BCR_MIIDATA) & 0xFFFF;
1356*438b5f69SJason King 	if (val == 0xFFFF) {
1357*438b5f69SJason King 		return (0);
1358*438b5f69SJason King 	}
1359*438b5f69SJason King 
1360*438b5f69SJason King 	if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
1361*438b5f69SJason King 	    pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
1362*438b5f69SJason King 	    pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr == -1)
1363*438b5f69SJason King 		pcnp->pcn_extphyaddr = phy;
1364*438b5f69SJason King 
1365*438b5f69SJason King 	return (val);
1366*438b5f69SJason King }
1367*438b5f69SJason King 
1368*438b5f69SJason King static void
pcn_mii_write(void * arg,uint8_t phy,uint8_t reg,uint16_t val)1369*438b5f69SJason King pcn_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
1370*438b5f69SJason King {
1371*438b5f69SJason King 	pcn_t		*pcnp = (pcn_t *)arg;
1372*438b5f69SJason King 
1373*438b5f69SJason King 	pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, reg | (phy << 5));
1374*438b5f69SJason King 	pcn_bcr_write(pcnp, PCN_BCR_MIIDATA, val);
1375*438b5f69SJason King }
1376*438b5f69SJason King 
1377*438b5f69SJason King static void
pcn_mii_notify(void * arg,link_state_t link)1378*438b5f69SJason King pcn_mii_notify(void *arg, link_state_t link)
1379*438b5f69SJason King {
1380*438b5f69SJason King 	pcn_t		*pcnp = (pcn_t *)arg;
1381*438b5f69SJason King 
1382*438b5f69SJason King 	mac_link_update(pcnp->pcn_mh, link);
1383*438b5f69SJason King }
1384*438b5f69SJason King 
1385*438b5f69SJason King static const pcn_type_t *
pcn_match(uint16_t vid,uint16_t did)1386*438b5f69SJason King pcn_match(uint16_t vid, uint16_t did)
1387*438b5f69SJason King {
1388*438b5f69SJason King 	const pcn_type_t	*t;
1389*438b5f69SJason King 
1390*438b5f69SJason King 	t = pcn_devs;
1391*438b5f69SJason King 	while (t->pcn_name != NULL) {
1392*438b5f69SJason King 		if ((vid == t->pcn_vid) && (did == t->pcn_did))
1393*438b5f69SJason King 			return (t);
1394*438b5f69SJason King 		t++;
1395*438b5f69SJason King 	}
1396*438b5f69SJason King 	return (NULL);
1397*438b5f69SJason King }
1398*438b5f69SJason King 
1399*438b5f69SJason King static void
pcn_getfactaddr(pcn_t * pcnp)1400*438b5f69SJason King pcn_getfactaddr(pcn_t *pcnp)
1401*438b5f69SJason King {
1402*438b5f69SJason King 	uint32_t addr[2];
1403*438b5f69SJason King 
1404*438b5f69SJason King 	addr[0] = CSR_READ_4(pcnp, PCN_IO32_APROM00);
1405*438b5f69SJason King 	addr[1] = CSR_READ_4(pcnp, PCN_IO32_APROM01);
1406*438b5f69SJason King 
1407*438b5f69SJason King 	bcopy(&addr[0], &pcnp->pcn_addr[0], sizeof (pcnp->pcn_addr));
1408*438b5f69SJason King }
1409*438b5f69SJason King 
1410*438b5f69SJason King static uint32_t
pcn_csr_read(pcn_t * pcnp,uint32_t reg)1411*438b5f69SJason King pcn_csr_read(pcn_t *pcnp, uint32_t reg)
1412*438b5f69SJason King {
1413*438b5f69SJason King 	uint32_t val;
1414*438b5f69SJason King 
1415*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1416*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
1417*438b5f69SJason King 	val = CSR_READ_4(pcnp, PCN_IO32_RDP);
1418*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1419*438b5f69SJason King 	return (val);
1420*438b5f69SJason King }
1421*438b5f69SJason King 
1422*438b5f69SJason King static uint16_t
pcn_csr_read16(pcn_t * pcnp,uint32_t reg)1423*438b5f69SJason King pcn_csr_read16(pcn_t *pcnp, uint32_t reg)
1424*438b5f69SJason King {
1425*438b5f69SJason King 	uint16_t val;
1426*438b5f69SJason King 
1427*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1428*438b5f69SJason King 	CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
1429*438b5f69SJason King 	val = CSR_READ_2(pcnp, PCN_IO16_RDP);
1430*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1431*438b5f69SJason King 	return (val);
1432*438b5f69SJason King }
1433*438b5f69SJason King 
1434*438b5f69SJason King static void
pcn_csr_write(pcn_t * pcnp,uint32_t reg,uint32_t val)1435*438b5f69SJason King pcn_csr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
1436*438b5f69SJason King {
1437*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1438*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
1439*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RDP, val);
1440*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1441*438b5f69SJason King }
1442*438b5f69SJason King 
1443*438b5f69SJason King static uint32_t
pcn_bcr_read(pcn_t * pcnp,uint32_t reg)1444*438b5f69SJason King pcn_bcr_read(pcn_t *pcnp, uint32_t reg)
1445*438b5f69SJason King {
1446*438b5f69SJason King 	uint32_t val;
1447*438b5f69SJason King 
1448*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1449*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
1450*438b5f69SJason King 	val = CSR_READ_4(pcnp, PCN_IO32_BDP);
1451*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1452*438b5f69SJason King 	return (val);
1453*438b5f69SJason King }
1454*438b5f69SJason King 
1455*438b5f69SJason King static uint16_t
pcn_bcr_read16(pcn_t * pcnp,uint32_t reg)1456*438b5f69SJason King pcn_bcr_read16(pcn_t *pcnp, uint32_t reg)
1457*438b5f69SJason King {
1458*438b5f69SJason King 	uint16_t val;
1459*438b5f69SJason King 
1460*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1461*438b5f69SJason King 	CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
1462*438b5f69SJason King 	val = CSR_READ_2(pcnp, PCN_IO16_BDP);
1463*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1464*438b5f69SJason King 	return (val);
1465*438b5f69SJason King }
1466*438b5f69SJason King 
1467*438b5f69SJason King static void
pcn_bcr_write(pcn_t * pcnp,uint32_t reg,uint32_t val)1468*438b5f69SJason King pcn_bcr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
1469*438b5f69SJason King {
1470*438b5f69SJason King 	mutex_enter(&pcnp->pcn_reglock);
1471*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
1472*438b5f69SJason King 	CSR_WRITE_4(pcnp, PCN_IO32_BDP, val);
1473*438b5f69SJason King 	mutex_exit(&pcnp->pcn_reglock);
1474*438b5f69SJason King }
1475*438b5f69SJason King 
1476*438b5f69SJason King static void
pcn_resetrings(pcn_t * pcnp)1477*438b5f69SJason King pcn_resetrings(pcn_t *pcnp)
1478*438b5f69SJason King {
1479*438b5f69SJason King 	int i;
1480*438b5f69SJason King 	uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO;
1481*438b5f69SJason King 
1482*438b5f69SJason King 	pcnp->pcn_rxhead = 0;
1483*438b5f69SJason King 	pcnp->pcn_txreclaim = 0;
1484*438b5f69SJason King 	pcnp->pcn_txsend = 0;
1485*438b5f69SJason King 	pcnp->pcn_txavail = PCN_TXRING;
1486*438b5f69SJason King 
1487*438b5f69SJason King 	/* reset rx descriptor values */
1488*438b5f69SJason King 	for (i = 0; i < PCN_RXRING; i++) {
1489*438b5f69SJason King 		pcn_rx_desc_t	*rmd = &pcnp->pcn_rxdescp[i];
1490*438b5f69SJason King 		pcn_buf_t	*rxb = pcnp->pcn_rxbufs[i];
1491*438b5f69SJason King 
1492*438b5f69SJason King 		rmd->pcn_rxlen = rmd->pcn_rsvd0 = 0;
1493*438b5f69SJason King 		rmd->pcn_rbaddr = rxb->pb_paddr;
1494*438b5f69SJason King 		rmd->pcn_bufsz = bufsz;
1495*438b5f69SJason King 		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
1496*438b5f69SJason King 	}
1497*438b5f69SJason King 	(void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0,
1498*438b5f69SJason King 	    PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV);
1499*438b5f69SJason King 
1500*438b5f69SJason King 	/* reset tx descriptor values */
1501*438b5f69SJason King 	for (i = 0; i < PCN_TXRING; i++) {
1502*438b5f69SJason King 		pcn_tx_desc_t	*txd = &pcnp->pcn_txdescp[i];
1503*438b5f69SJason King 		pcn_buf_t	*txb = pcnp->pcn_txbufs[i];
1504*438b5f69SJason King 
1505*438b5f69SJason King 		txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0;
1506*438b5f69SJason King 		txd->pcn_tbaddr = txb->pb_paddr;
1507*438b5f69SJason King 	}
1508*438b5f69SJason King 	(void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0,
1509*438b5f69SJason King 	    PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV);
1510*438b5f69SJason King 
1511*438b5f69SJason King 	/* set addresses of decriptors */
1512*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
1513*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
1514*438b5f69SJason King 	    (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);
1515*438b5f69SJason King 
1516*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
1517*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
1518*438b5f69SJason King 	    (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);
1519*438b5f69SJason King 
1520*438b5f69SJason King 	/* set the ring sizes */
1521*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
1522*438b5f69SJason King 	pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
1523*438b5f69SJason King }
1524*438b5f69SJason King 
1525*438b5f69SJason King static void
pcn_destroybuf(pcn_buf_t * buf)1526*438b5f69SJason King pcn_destroybuf(pcn_buf_t *buf)
1527*438b5f69SJason King {
1528*438b5f69SJason King 	if (buf == NULL)
1529*438b5f69SJason King 		return;
1530*438b5f69SJason King 
1531*438b5f69SJason King 	if (buf->pb_paddr)
1532*438b5f69SJason King 		(void) ddi_dma_unbind_handle(buf->pb_dmah);
1533*438b5f69SJason King 	if (buf->pb_acch)
1534*438b5f69SJason King 		ddi_dma_mem_free(&buf->pb_acch);
1535*438b5f69SJason King 	if (buf->pb_dmah)
1536*438b5f69SJason King 		ddi_dma_free_handle(&buf->pb_dmah);
1537*438b5f69SJason King 	kmem_free(buf, sizeof (*buf));
1538*438b5f69SJason King }
1539*438b5f69SJason King 
1540*438b5f69SJason King static pcn_buf_t *
pcn_allocbuf(pcn_t * pcnp)1541*438b5f69SJason King pcn_allocbuf(pcn_t *pcnp)
1542*438b5f69SJason King {
1543*438b5f69SJason King 	pcn_buf_t		*buf;
1544*438b5f69SJason King 	size_t			len;
1545*438b5f69SJason King 	unsigned		ccnt;
1546*438b5f69SJason King 	ddi_dma_cookie_t	dmac;
1547*438b5f69SJason King 
1548*438b5f69SJason King 	buf = kmem_zalloc(sizeof (*buf), KM_SLEEP);
1549*438b5f69SJason King 
1550*438b5f69SJason King 	if (ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
1551*438b5f69SJason King 	    NULL, &buf->pb_dmah) != DDI_SUCCESS) {
1552*438b5f69SJason King 		kmem_free(buf, sizeof (*buf));
1553*438b5f69SJason King 		return (NULL);
1554*438b5f69SJason King 	}
1555*438b5f69SJason King 
1556*438b5f69SJason King 	if (ddi_dma_mem_alloc(buf->pb_dmah, PCN_BUFSZ, &pcn_bufattr,
1557*438b5f69SJason King 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->pb_buf, &len,
1558*438b5f69SJason King 	    &buf->pb_acch) != DDI_SUCCESS) {
1559*438b5f69SJason King 		pcn_destroybuf(buf);
1560*438b5f69SJason King 		return (NULL);
1561*438b5f69SJason King 	}
1562*438b5f69SJason King 
1563*438b5f69SJason King 	if (ddi_dma_addr_bind_handle(buf->pb_dmah, NULL, buf->pb_buf, len,
1564*438b5f69SJason King 	    DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
1565*438b5f69SJason King 	    &ccnt) != DDI_DMA_MAPPED) {
1566*438b5f69SJason King 		pcn_destroybuf(buf);
1567*438b5f69SJason King 		return (NULL);
1568*438b5f69SJason King 	}
1569*438b5f69SJason King 	buf->pb_paddr = dmac.dmac_address;
1570*438b5f69SJason King 
1571*438b5f69SJason King 	return (buf);
1572*438b5f69SJason King }
1573*438b5f69SJason King 
1574*438b5f69SJason King static int
pcn_alloctxring(pcn_t * pcnp)1575*438b5f69SJason King pcn_alloctxring(pcn_t *pcnp)
1576*438b5f69SJason King {
1577*438b5f69SJason King 	int			rval;
1578*438b5f69SJason King 	int			i;
1579*438b5f69SJason King 	size_t			size;
1580*438b5f69SJason King 	size_t			len;
1581*438b5f69SJason King 	ddi_dma_cookie_t	dmac;
1582*438b5f69SJason King 	unsigned		ncookies;
1583*438b5f69SJason King 	caddr_t			kaddr;
1584*438b5f69SJason King 
1585*438b5f69SJason King 	size = PCN_TXRING * sizeof (pcn_tx_desc_t);
1586*438b5f69SJason King 
1587*438b5f69SJason King 	rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
1588*438b5f69SJason King 	    NULL, &pcnp->pcn_txdesc_dmah);
1589*438b5f69SJason King 	if (rval != DDI_SUCCESS) {
1590*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for tx "
1591*438b5f69SJason King 		    "descriptors");
1592*438b5f69SJason King 		return (DDI_FAILURE);
1593*438b5f69SJason King 	}
1594*438b5f69SJason King 
1595*438b5f69SJason King 	rval = ddi_dma_mem_alloc(pcnp->pcn_txdesc_dmah, size, &pcn_devattr,
1596*438b5f69SJason King 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
1597*438b5f69SJason King 	    &pcnp->pcn_txdesc_acch);
1598*438b5f69SJason King 	if (rval != DDI_SUCCESS) {
1599*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for tx "
1600*438b5f69SJason King 		    "descriptors");
1601*438b5f69SJason King 		return (DDI_FAILURE);
1602*438b5f69SJason King 	}
1603*438b5f69SJason King 
1604*438b5f69SJason King 	rval = ddi_dma_addr_bind_handle(pcnp->pcn_txdesc_dmah, NULL, kaddr,
1605*438b5f69SJason King 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
1606*438b5f69SJason King 	    &ncookies);
1607*438b5f69SJason King 	if (rval != DDI_DMA_MAPPED) {
1608*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to bind DMA for tx "
1609*438b5f69SJason King 		    "descriptors");
1610*438b5f69SJason King 		return (DDI_FAILURE);
1611*438b5f69SJason King 	}
1612*438b5f69SJason King 
1613*438b5f69SJason King 	ASSERT(ncookies == 1);
1614*438b5f69SJason King 
1615*438b5f69SJason King 	pcnp->pcn_txdesc_paddr = dmac.dmac_address;
1616*438b5f69SJason King 	pcnp->pcn_txdescp = (void *)kaddr;
1617*438b5f69SJason King 
1618*438b5f69SJason King 	pcnp->pcn_txbufs = kmem_zalloc(PCN_TXRING * sizeof (pcn_buf_t *),
1619*438b5f69SJason King 	    KM_SLEEP);
1620*438b5f69SJason King 
1621*438b5f69SJason King 	for (i = 0; i < PCN_TXRING; i++) {
1622*438b5f69SJason King 		pcn_buf_t *txb = pcn_allocbuf(pcnp);
1623*438b5f69SJason King 		if (txb == NULL)
1624*438b5f69SJason King 			return (DDI_FAILURE);
1625*438b5f69SJason King 		pcnp->pcn_txbufs[i] = txb;
1626*438b5f69SJason King 	}
1627*438b5f69SJason King 
1628*438b5f69SJason King 	return (DDI_SUCCESS);
1629*438b5f69SJason King }
1630*438b5f69SJason King 
1631*438b5f69SJason King static int
pcn_allocrxring(pcn_t * pcnp)1632*438b5f69SJason King pcn_allocrxring(pcn_t *pcnp)
1633*438b5f69SJason King {
1634*438b5f69SJason King 	int			rval;
1635*438b5f69SJason King 	int			i;
1636*438b5f69SJason King 	size_t			len;
1637*438b5f69SJason King 	size_t			size;
1638*438b5f69SJason King 	ddi_dma_cookie_t	dmac;
1639*438b5f69SJason King 	unsigned		ncookies;
1640*438b5f69SJason King 	caddr_t			kaddr;
1641*438b5f69SJason King 
1642*438b5f69SJason King 	size = PCN_RXRING * sizeof (pcn_rx_desc_t);
1643*438b5f69SJason King 
1644*438b5f69SJason King 	rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dmadesc_attr,
1645*438b5f69SJason King 	    DDI_DMA_SLEEP, NULL, &pcnp->pcn_rxdesc_dmah);
1646*438b5f69SJason King 	if (rval != DDI_SUCCESS) {
1647*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for rx "
1648*438b5f69SJason King 		    "descriptors");
1649*438b5f69SJason King 		return (DDI_FAILURE);
1650*438b5f69SJason King 	}
1651*438b5f69SJason King 
1652*438b5f69SJason King 	rval = ddi_dma_mem_alloc(pcnp->pcn_rxdesc_dmah, size, &pcn_devattr,
1653*438b5f69SJason King 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
1654*438b5f69SJason King 	    &pcnp->pcn_rxdesc_acch);
1655*438b5f69SJason King 	if (rval != DDI_SUCCESS) {
1656*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for rx "
1657*438b5f69SJason King 		    "descriptors");
1658*438b5f69SJason King 		return (DDI_FAILURE);
1659*438b5f69SJason King 	}
1660*438b5f69SJason King 
1661*438b5f69SJason King 	rval = ddi_dma_addr_bind_handle(pcnp->pcn_rxdesc_dmah, NULL, kaddr,
1662*438b5f69SJason King 	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
1663*438b5f69SJason King 	    &ncookies);
1664*438b5f69SJason King 	if (rval != DDI_DMA_MAPPED) {
1665*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "unable to bind DMA for rx "
1666*438b5f69SJason King 		    "descriptors");
1667*438b5f69SJason King 		return (DDI_FAILURE);
1668*438b5f69SJason King 	}
1669*438b5f69SJason King 
1670*438b5f69SJason King 	ASSERT(ncookies == 1);
1671*438b5f69SJason King 
1672*438b5f69SJason King 	pcnp->pcn_rxdesc_paddr = dmac.dmac_address;
1673*438b5f69SJason King 	pcnp->pcn_rxdescp = (void *)kaddr;
1674*438b5f69SJason King 
1675*438b5f69SJason King 	pcnp->pcn_rxbufs = kmem_zalloc(PCN_RXRING * sizeof (pcn_buf_t *),
1676*438b5f69SJason King 	    KM_SLEEP);
1677*438b5f69SJason King 
1678*438b5f69SJason King 	for (i = 0; i < PCN_RXRING; i++) {
1679*438b5f69SJason King 		pcn_buf_t *rxb = pcn_allocbuf(pcnp);
1680*438b5f69SJason King 		if (rxb == NULL)
1681*438b5f69SJason King 			return (DDI_FAILURE);
1682*438b5f69SJason King 		pcnp->pcn_rxbufs[i] = rxb;
1683*438b5f69SJason King 	}
1684*438b5f69SJason King 
1685*438b5f69SJason King 	return (DDI_SUCCESS);
1686*438b5f69SJason King }
1687*438b5f69SJason King 
1688*438b5f69SJason King static void
pcn_freetxring(pcn_t * pcnp)1689*438b5f69SJason King pcn_freetxring(pcn_t *pcnp)
1690*438b5f69SJason King {
1691*438b5f69SJason King 	int	i;
1692*438b5f69SJason King 
1693*438b5f69SJason King 	if (pcnp->pcn_txbufs) {
1694*438b5f69SJason King 		for (i = 0; i < PCN_TXRING; i++)
1695*438b5f69SJason King 			pcn_destroybuf(pcnp->pcn_txbufs[i]);
1696*438b5f69SJason King 
1697*438b5f69SJason King 		kmem_free(pcnp->pcn_txbufs, PCN_TXRING * sizeof (pcn_buf_t *));
1698*438b5f69SJason King 	}
1699*438b5f69SJason King 
1700*438b5f69SJason King 	if (pcnp->pcn_txdesc_paddr)
1701*438b5f69SJason King 		(void) ddi_dma_unbind_handle(pcnp->pcn_txdesc_dmah);
1702*438b5f69SJason King 	if (pcnp->pcn_txdesc_acch)
1703*438b5f69SJason King 		ddi_dma_mem_free(&pcnp->pcn_txdesc_acch);
1704*438b5f69SJason King 	if (pcnp->pcn_txdesc_dmah)
1705*438b5f69SJason King 		ddi_dma_free_handle(&pcnp->pcn_txdesc_dmah);
1706*438b5f69SJason King }
1707*438b5f69SJason King 
1708*438b5f69SJason King static void
pcn_freerxring(pcn_t * pcnp)1709*438b5f69SJason King pcn_freerxring(pcn_t *pcnp)
1710*438b5f69SJason King {
1711*438b5f69SJason King 	int	i;
1712*438b5f69SJason King 
1713*438b5f69SJason King 	if (pcnp->pcn_rxbufs) {
1714*438b5f69SJason King 		for (i = 0; i < PCN_RXRING; i++)
1715*438b5f69SJason King 			pcn_destroybuf(pcnp->pcn_rxbufs[i]);
1716*438b5f69SJason King 
1717*438b5f69SJason King 		kmem_free(pcnp->pcn_rxbufs, PCN_RXRING * sizeof (pcn_buf_t *));
1718*438b5f69SJason King 	}
1719*438b5f69SJason King 
1720*438b5f69SJason King 	if (pcnp->pcn_rxdesc_paddr)
1721*438b5f69SJason King 		(void) ddi_dma_unbind_handle(pcnp->pcn_rxdesc_dmah);
1722*438b5f69SJason King 	if (pcnp->pcn_rxdesc_acch)
1723*438b5f69SJason King 		ddi_dma_mem_free(&pcnp->pcn_rxdesc_acch);
1724*438b5f69SJason King 	if (pcnp->pcn_rxdesc_dmah)
1725*438b5f69SJason King 		ddi_dma_free_handle(&pcnp->pcn_rxdesc_dmah);
1726*438b5f69SJason King }
1727*438b5f69SJason King 
1728*438b5f69SJason King static int
pcn_set_chipid(pcn_t * pcnp,uint32_t conf_id)1729*438b5f69SJason King pcn_set_chipid(pcn_t *pcnp, uint32_t conf_id)
1730*438b5f69SJason King {
1731*438b5f69SJason King 	char *name = NULL;
1732*438b5f69SJason King 	uint32_t chipid;
1733*438b5f69SJason King 
1734*438b5f69SJason King 	/*
1735*438b5f69SJason King 	 * Note: we can *NOT* put the chip into 32-bit mode yet. If a
1736*438b5f69SJason King 	 * lance ethernet device is present and pcn tries to attach, it can
1737*438b5f69SJason King 	 * hang the device (requiring a hardware reset), since they only work
1738*438b5f69SJason King 	 * in 16-bit mode.
1739*438b5f69SJason King 	 *
1740*438b5f69SJason King 	 * The solution is check using 16-bit operations first, and determine
1741*438b5f69SJason King 	 * if 32-bit mode operations are supported.
1742*438b5f69SJason King 	 *
1743*438b5f69SJason King 	 * The safest way to do this is to read the PCI subsystem ID from
1744*438b5f69SJason King 	 * BCR23/24 and compare that with the value read from PCI config
1745*438b5f69SJason King 	 * space.
1746*438b5f69SJason King 	 */
1747*438b5f69SJason King 	chipid = pcn_bcr_read16(pcnp, PCN_BCR_PCISUBSYSID);
1748*438b5f69SJason King 	chipid <<= 16;
1749*438b5f69SJason King 	chipid |= pcn_bcr_read16(pcnp, PCN_BCR_PCISUBVENID);
1750*438b5f69SJason King 
1751*438b5f69SJason King 	/*
1752*438b5f69SJason King 	 * The test for 0x10001000 is a hack to pacify VMware, who's
1753*438b5f69SJason King 	 * pseudo-PCnet interface is broken. Reading the subsystem register
1754*438b5f69SJason King 	 * from PCI config space yields 0x00000000 while reading the same value
1755*438b5f69SJason King 	 * from I/O space yields 0x10001000. It's not supposed to be that way.
1756*438b5f69SJason King 	 */
1757*438b5f69SJason King 	if (chipid == conf_id || chipid == 0x10001000) {
1758*438b5f69SJason King 		/* We're in 16-bit mode. */
1759*438b5f69SJason King 		chipid = pcn_csr_read16(pcnp, PCN_CSR_CHIPID1);
1760*438b5f69SJason King 		chipid <<= 16;
1761*438b5f69SJason King 		chipid |= pcn_csr_read16(pcnp, PCN_CSR_CHIPID0);
1762*438b5f69SJason King 	} else {
1763*438b5f69SJason King 		chipid = pcn_csr_read(pcnp, PCN_CSR_CHIPID1);
1764*438b5f69SJason King 		chipid <<= 16;
1765*438b5f69SJason King 		chipid |= pcn_csr_read(pcnp, PCN_CSR_CHIPID0);
1766*438b5f69SJason King 	}
1767*438b5f69SJason King 
1768*438b5f69SJason King 	chipid = CHIPID_PARTID(chipid);
1769*438b5f69SJason King 
1770*438b5f69SJason King 	/* Set default value and override as needed */
1771*438b5f69SJason King 	switch (chipid) {
1772*438b5f69SJason King 	case Am79C970:
1773*438b5f69SJason King 		name = "Am79C970 PCnet-PCI";
1774*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
1775*438b5f69SJason King 		return (DDI_FAILURE);
1776*438b5f69SJason King 	case Am79C970A:
1777*438b5f69SJason King 		name = "Am79C970A PCnet-PCI II";
1778*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
1779*438b5f69SJason King 		return (DDI_FAILURE);
1780*438b5f69SJason King 	case Am79C971:
1781*438b5f69SJason King 		name = "Am79C971 PCnet-FAST";
1782*438b5f69SJason King 		break;
1783*438b5f69SJason King 	case Am79C972:
1784*438b5f69SJason King 		name = "Am79C972 PCnet-FAST+";
1785*438b5f69SJason King 		break;
1786*438b5f69SJason King 	case Am79C973:
1787*438b5f69SJason King 		name = "Am79C973 PCnet-FAST III";
1788*438b5f69SJason King 		break;
1789*438b5f69SJason King 	case Am79C975:
1790*438b5f69SJason King 		name = "Am79C975 PCnet-FAST III";
1791*438b5f69SJason King 		break;
1792*438b5f69SJason King 	case Am79C976:
1793*438b5f69SJason King 		name = "Am79C976";
1794*438b5f69SJason King 		break;
1795*438b5f69SJason King 	case Am79C978:
1796*438b5f69SJason King 		name = "Am79C978";
1797*438b5f69SJason King 		break;
1798*438b5f69SJason King 	default:
1799*438b5f69SJason King 		name = "Unknown";
1800*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "Unknown chip id 0x%x", chipid);
1801*438b5f69SJason King 	}
1802*438b5f69SJason King 
1803*438b5f69SJason King 	if (ddi_prop_update_string(DDI_DEV_T_NONE, pcnp->pcn_dip, "chipid",
1804*438b5f69SJason King 	    name) != DDI_SUCCESS) {
1805*438b5f69SJason King 		pcn_error(pcnp->pcn_dip, "Unable to set chipid property");
1806*438b5f69SJason King 		return (DDI_FAILURE);
1807*438b5f69SJason King 	}
1808*438b5f69SJason King 
1809*438b5f69SJason King 	return (DDI_SUCCESS);
1810*438b5f69SJason King }
1811*438b5f69SJason King 
1812*438b5f69SJason King static void
pcn_error(dev_info_t * dip,char * fmt,...)1813*438b5f69SJason King pcn_error(dev_info_t *dip, char *fmt, ...)
1814*438b5f69SJason King {
1815*438b5f69SJason King 	va_list	ap;
1816*438b5f69SJason King 	char	buf[256];
1817*438b5f69SJason King 
1818*438b5f69SJason King 	va_start(ap, fmt);
1819*438b5f69SJason King 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
1820*438b5f69SJason King 	va_end(ap);
1821*438b5f69SJason King 
1822*438b5f69SJason King 	if (dip)
1823*438b5f69SJason King 		cmn_err(CE_WARN, "%s%d: %s", ddi_driver_name(dip),
1824*438b5f69SJason King 		    ddi_get_instance(dip), buf);
1825*438b5f69SJason King 	else
1826*438b5f69SJason King 		cmn_err(CE_WARN, "pcn: %s", buf);
1827*438b5f69SJason King }
1828