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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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