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