/pcn/src/pcn.c
https://bitbucket.org/0xffea/driver-gate · C · 1748 lines · 1288 code · 337 blank · 123 comment · 204 complexity · 16530b3e839acff0e00f58e39f0a076f MD5 · raw file
- /*
- * Copyright (c) 2000 Berkeley Software Design, Inc.
- * Copyright (c) 1997, 1998, 1999, 2000
- * Bill Paul <wpaul@osd.bsdi.com>. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Bill Paul.
- * 4. Neither the name of the author nor the names of any co-contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <sys/varargs.h>
- #include <sys/types.h>
- #include <sys/modctl.h>
- #include <sys/devops.h>
- #include <sys/stream.h>
- #include <sys/strsun.h>
- #include <sys/cmn_err.h>
- #include <sys/ethernet.h>
- #include <sys/kmem.h>
- #include <sys/crc32.h>
- #include <sys/mii.h>
- #include <sys/miiregs.h>
- #include <sys/mac.h>
- #include <sys/mac_ether.h>
- #include <sys/ddi.h>
- #include <sys/sunddi.h>
- #include <sys/vlan.h>
- #include <sys/pci.h>
- #include <sys/conf.h>
- #include "pcn.h"
- #include "pcnimpl.h"
- #define ETHERVLANMTU (ETHERMAX + 4)
- #define CSR_WRITE_4(pcnp, reg, val) \
- ddi_put32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg), val)
- #define CSR_WRITE_2(pcnp, reg, val) \
- ddi_put16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg), val)
- #define CSR_READ_4(pcnp, reg) \
- ddi_get32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg))
- #define CSR_READ_2(pcnp, reg) \
- ddi_get16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg))
- #define PCN_CSR_SETBIT(pcnp, reg, x) \
- pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) | (x))
- #define PCN_CSR_CLRBIT(pcnp, reg, x) \
- pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) & ~(x))
- #define PCN_BCR_SETBIT(pncp, reg, x) \
- pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) | (x))
- #define PCN_BCR_CLRBIT(pcnp, reg, x) \
- pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) & ~(x))
- static int pcn_attach(dev_info_t *, ddi_attach_cmd_t);
- static int pcn_detach(dev_info_t *, ddi_detach_cmd_t);
- static int pcn_resume(dev_info_t *);
- static int pcn_quiesce(dev_info_t *);
- static int pcn_setup_intr(pcn_t *);
- static int pcn_teardown_intr(pcn_t *);
- static int pcn_m_unicast(void *, const uint8_t *);
- static int pcn_m_multicast(void *, boolean_t, const uint8_t *);
- static int pcn_m_promisc(void *, boolean_t);
- static mblk_t *pcn_m_tx(void *, mblk_t *);
- static void pcn_m_ioctl(void *, queue_t *, mblk_t *);
- static int pcn_m_stat(void *, uint_t, uint64_t *);
- static int pcn_m_start(void *);
- static void pcn_m_stop(void *);
- static int pcn_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
- uint_t, void *, uint_t *);
- static int pcn_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
- const void *);
- static unsigned pcn_intr(caddr_t, caddr_t);
- static uint16_t pcn_mii_read(void *, uint8_t, uint8_t);
- static void pcn_mii_write(void *, uint8_t, uint8_t, uint16_t);
- static void pcn_mii_notify(void *, link_state_t);
- static uint32_t pcn_csr_read(pcn_t *, uint32_t);
- static uint16_t pcn_csr_read16(pcn_t *, uint32_t);
- static void pcn_csr_write(pcn_t *, uint32_t, uint32_t);
- static uint32_t pcn_bcr_read(pcn_t *, uint32_t);
- static uint16_t pcn_bcr_read16(pcn_t *, uint32_t);
- static void pcn_bcr_write(pcn_t *, uint32_t, uint32_t);
- static boolean_t pcn_send(pcn_t *, mblk_t *);
- static int pcn_allocrxring(pcn_t *);
- static int pcn_alloctxring(pcn_t *);
- static void pcn_freetxring(pcn_t *);
- static void pcn_freerxring(pcn_t *);
- static void pcn_resetrings(pcn_t *);
- static int pcn_initialize(pcn_t *);
- static void pcn_enableinterrupts(pcn_t *);
- static void pcn_disableinterrupts(pcn_t *);
- static mblk_t *pcn_receive(pcn_t *);
- static void pcn_resetall(pcn_t *);
- static void pcn_startall(pcn_t *);
- static void pcn_stopall(pcn_t *);
- static void pcn_reclaim(pcn_t *);
- static void pcn_setrxfilt(pcn_t *);
- static void pcn_getfactaddr(pcn_t *);
- static int pcn_set_chipid(pcn_t *, uint32_t);
- static const pcn_type_t *pcn_match(uint16_t, uint16_t);
- static char *pcn_chipid_name(uint32_t);
- static void pcn_error(dev_info_t *, char *, ...);
- void *pcn_ssp = NULL;
- static uchar_t pcn_broadcast[ETHERADDRL] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
- };
- static const pcn_type_t pcn_devs[] = {
- { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" },
- { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" },
- { 0, 0, NULL }
- };
- static const struct pcn_chipid {
- uint32_t id;
- char *name;
- } pcn_chipid[] = {
- { Am79C971, "Am79C971" },
- { Am79C972, "Am79C972" },
- { Am79C973, "Am79C973" },
- { Am79C978, "Am79C978" },
- { Am79C975, "Am79C975" },
- { Am79C976, "Am79C976" },
- { 0, NULL },
- };
- static mii_ops_t pcn_mii_ops = {
- MII_OPS_VERSION,
- pcn_mii_read,
- pcn_mii_write,
- pcn_mii_notify,
- NULL
- };
- static mac_callbacks_t pcn_m_callbacks = {
- MC_IOCTL | MC_SETPROP | MC_GETPROP,
- pcn_m_stat,
- pcn_m_start,
- pcn_m_stop,
- pcn_m_promisc,
- pcn_m_multicast,
- pcn_m_unicast,
- pcn_m_tx,
- pcn_m_ioctl,
- NULL, /* mc_getcapab */
- NULL, /* mc_open */
- NULL, /* mc_close */
- pcn_m_setprop,
- pcn_m_getprop
- };
- DDI_DEFINE_STREAM_OPS(pcn_devops, nulldev, nulldev, pcn_attach, pcn_detach,
- nodev, NULL, D_MP, NULL, pcn_quiesce);
- static struct modldrv pcn_modldrv = {
- &mod_driverops,
- "AMD PCnet",
- &pcn_devops
- };
- static struct modlinkage pcn_modlinkage = {
- MODREV_1,
- { &pcn_modldrv, NULL }
- };
- static ddi_device_acc_attr_t pcn_devattr = {
- DDI_DEVICE_ATTR_V0,
- DDI_STRUCTURE_LE_ACC,
- DDI_STRICTORDER_ACC
- };
- static ddi_device_acc_attr_t pcn_bufattr = {
- DDI_DEVICE_ATTR_V0,
- DDI_NEVERSWAP_ACC,
- DDI_STRICTORDER_ACC
- };
- static ddi_dma_attr_t pcn_dma_attr = {
- DMA_ATTR_V0, /* dm_attr_version */
- 0, /* dma_attr_addr_lo */
- 0xFFFFFFFFU, /* dma_attr_addr_hi */
- 0x7FFFFFFFU, /* dma_attr_count_max */
- 4, /* dma_attr_align */
- 0x3F, /* dma_attr_burstsizes */
- 1, /* dma_attr_minxfer */
- 0xFFFFFFFFU, /* dma_attr_maxxfer */
- 0xFFFFFFFFU, /* dma_attr_seg */
- 1, /* dma_attr_sgllen */
- 1, /* dma_attr_granular */
- 0 /* dma_attr_flags */
- };
- static ddi_dma_attr_t pcn_dmadesc_attr = {
- DMA_ATTR_V0, /* dm_attr_version */
- 0, /* dma_attr_addr_lo */
- 0xFFFFFFFFU, /* dma_attr_addr_hi */
- 0x7FFFFFFFU, /* dma_attr_count_max */
- 16, /* dma_attr_align */
- 0x3F, /* dma_attr_burstsizes */
- 1, /* dma_attr_minxfer */
- 0xFFFFFFFFU, /* dma_attr_maxxfer */
- 0xFFFFFFFFU, /* dma_attr_seg */
- 1, /* dma_attr_sgllen */
- 1, /* dma_attr_granular */
- 0 /* dma_attr_flags */
- };
- /*
- * DDI entry points
- */
- int
- _init(void)
- {
- int rc;
- if ((rc = ddi_soft_state_init(&pcn_ssp, sizeof (pcn_t), 1)) != 0)
- return (rc);
- mac_init_ops(&pcn_devops, "pcn");
- if ((rc = mod_install(&pcn_modlinkage)) != DDI_SUCCESS) {
- mac_fini_ops(&pcn_devops);
- ddi_soft_state_fini(&pcn_ssp);
- }
- return (rc);
- }
- int
- _fini(void)
- {
- int rc;
- if ((rc = mod_remove(&pcn_modlinkage)) == DDI_SUCCESS) {
- mac_fini_ops(&pcn_devops);
- ddi_soft_state_fini(&pcn_ssp);
- }
- return (rc);
- }
- int
- _info(struct modinfo *modinfop)
- {
- return (mod_info(&pcn_modlinkage, modinfop));
- }
- int
- pcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
- {
- pcn_t *pcnp;
- mac_register_t *macp;
- const pcn_type_t *pcn_type;
- int instance = ddi_get_instance(dip);
- int rc;
- ddi_acc_handle_t pci;
- uint32_t chipid;
- uint16_t venid;
- uint16_t devid;
- uint16_t svid;
- uint16_t ssid;
- uint16_t cachesize;
- switch (cmd) {
- case DDI_RESUME:
- return (pcn_resume(dip));
- case DDI_ATTACH:
- break;
- default:
- return (DDI_FAILURE);
- }
- if (ddi_slaveonly(dip) == DDI_SUCCESS) {
- pcn_error(dip, "slot does not support PCI bus-master");
- return (DDI_FAILURE);
- }
- if (ddi_intr_hilevel(dip, 0) != 0) {
- pcn_error(dip, "hilevel interrupts not supported");
- return (DDI_FAILURE);
- }
- if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
- pcn_error(dip, "unable to setup PCI config handle");
- return (DDI_FAILURE);
- }
- venid = pci_config_get16(pci, PCI_CONF_VENID);
- devid = pci_config_get16(pci, PCI_CONF_DEVID);
- svid = pci_config_get16(pci, PCI_CONF_SUBVENID);
- ssid = pci_config_get16(pci, PCI_CONF_SUBSYSID);
- if ((pcn_type = pcn_match(venid, devid)) == NULL) {
- pci_config_teardown(&pci);
- pcn_error(dip, "Unable to identify PCI card");
- return (DDI_FAILURE);
- }
- if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
- pcn_type->pcn_name) != DDI_PROP_SUCCESS) {
- pci_config_teardown(&pci);
- pcn_error(dip, "Unable to create model property");
- return (DDI_FAILURE);
- }
- cachesize = pci_config_get8(pci, PCI_CONF_CACHE_LINESZ);
- if (ddi_soft_state_zalloc(pcn_ssp, instance) != DDI_SUCCESS) {
- pcn_error(dip, "Unable to allocate soft state");
- pci_config_teardown(&pci);
- return (DDI_FAILURE);
- }
- pcnp = ddi_get_soft_state(pcn_ssp, instance);
- pcnp->pcn_dip = dip;
- pcnp->pcn_instance = instance;
- if (pcn_setup_intr(pcnp) != DDI_SUCCESS) {
- ddi_soft_state_free(pcn_ssp, instance);
- pci_config_teardown(&pci);
- return (DDI_FAILURE);
- }
- mutex_init(&pcnp->pcn_xmtlock, NULL, MUTEX_DRIVER,
- DDI_INTR_PRI(pcnp->pcn_int_pri));
- mutex_init(&pcnp->pcn_intrlock, NULL, MUTEX_DRIVER,
- DDI_INTR_PRI(pcnp->pcn_int_pri));
- /*
- * Enable bus master, IO space, and memory space accesses
- */
- pci_config_put16(pci, PCI_CONF_COMM,
- pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_ME | PCI_COMM_MAE);
- pci_config_teardown(&pci);
- if ((pcnp->pcn_mii = mii_alloc(pcnp, dip, &pcn_mii_ops)) == NULL)
- goto fail;
- mii_set_pauseable(pcnp->pcn_mii, B_TRUE, B_TRUE);
- if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcnp->pcn_regs, 0, 0,
- &pcn_devattr, &pcnp->pcn_regshandle)) {
- pcn_error(dip, "ddi_regs_map_setup failed");
- goto fail;
- }
- if (pcn_set_chipid(pcnp, (uint32_t)ssid << 16 | (uint32_t)svid) !=
- DDI_SUCCESS) {
- goto fail;
- }
- if ((pcn_allocrxring(pcnp) != DDI_SUCCESS) ||
- (pcn_alloctxring(pcnp) != DDI_SUCCESS)) {
- pcn_error(dip, "unable to allocate DMA resources");
- goto fail;
- }
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- rc = pcn_initialize(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- if (rc != DDI_SUCCESS)
- goto fail;
- pcn_getfactaddr(pcnp);
- pcnp->pcn_promisc = B_FALSE;
- (void) pcn_m_unicast(pcnp, pcnp->pcn_addr);
- /* XXX: do we need to add the broadcast addr to the addr filter? */
- if ((rc = ddi_intr_add_handler(pcnp->pcn_htable, pcn_intr,
- (caddr_t)pcnp, NULL)) != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "failed to add interrupt handler");
- return (DDI_FAILURE);
- }
- if (ddi_intr_enable(pcnp->pcn_htable) != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "failed to enable interrupt");
- goto fail;
- }
- if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
- pcn_error(pcnp->pcn_dip, "mac_alloc failed");
- goto fail;
- }
- macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
- macp->m_driver = pcnp;
- macp->m_dip = dip;
- macp->m_src_addr = pcnp->pcn_addr;
- macp->m_callbacks = &pcn_m_callbacks;
- macp->m_min_sdu = 0;
- macp->m_max_sdu = ETHERMTU;
- macp->m_margin = VLAN_TAGSZ;
- if (mac_register(macp, &pcnp->pcn_mh) == DDI_SUCCESS) {
- mac_free(macp);
- ddi_report_dev(dip);
- return (DDI_SUCCESS);
- }
- mac_free(macp);
- fail:
- ddi_intr_free(pcnp->pcn_htable);
- ddi_soft_state_free(pcn_ssp, instance);
- return (DDI_FAILURE);
- }
- static int
- pcn_set_chipid(pcn_t *pcnp, uint32_t conf_id)
- {
- uint32_t chipid;
- /*
- * Note: we can *NOT* put the chip into
- * 32-bit mode yet. The le(4) driver will only
- * work in 16-bit mode, and once the chip
- * goes into 32-bit mode, the only way to
- * get it out again is with a hardware reset.
- * So if pcn_probe() is called before the
- * le(4) driver's probe routine, the chip will
- * be locked into 32-bit operation and the
- * le(4) driver will be unable to attach to it.
- * Note II: if the chip happens to already
- * be in 32-bit mode, we still need to check
- * the chip ID, but first we have to detect
- * 32-bit mode using only 16-bit operations.
- * The safest way to do this is to read the
- * PCI subsystem ID from BCR23/24 and compare
- * that with the value read from PCI config
- * space.
- */
- chipid = pcn_bcr_read16(pcnp, PCN_BCR_PCISUBSYSID);
- chipid <<= 16;
- chipid |= pcn_bcr_read16(pcnp, PCN_BCR_PCISUBVENID);
- /*
- * Note III: the test for 0x10001000 is a hack to
- * pacify VMware, who's pseudo-PCnet interface is
- * broken. Reading the subsystem register from PCI
- * config space yields 0x00000000 while reading the
- * same value from I/O space yields 0x10001000. It's
- * not supposed to be that way.
- */
- if (chipid == conf_id || chipid == 0x10001000) {
- /* We're in 16-bit mode. */
- chipid = pcn_csr_read16(pcnp, PCN_CSR_CHIPID1);
- chipid <<= 16;
- chipid |= pcn_csr_read16(pcnp, PCN_CSR_CHIPID0);
- } else {
- chipid = pcn_csr_read(pcnp, PCN_CSR_CHIPID1);
- chipid <<= 16;
- chipid |= pcn_csr_read(pcnp, PCN_CSR_CHIPID0);
- }
- switch ((chipid >> 12) & PART_MASK) {
- case Am79C971:
- case Am79C972:
- case Am79C973:
- case Am79C975:
- case Am79C976:
- case Am79C978:
- break;
- default:
- pcn_error(pcnp->pcn_dip, "Unknown chip id 0x%u",
- (chipid >> 12) & PART_MASK);
- return (DDI_FAILURE);
- }
- if (ddi_prop_update_string(DDI_DEV_T_NONE, pcnp->pcn_dip, "chipid",
- pcn_chipid_name(chipid)) != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "Unable to set chipid property");
- return (DDI_FAILURE);
- }
- return (DDI_SUCCESS);
- }
- static int
- pcn_setup_intr(pcn_t *pcnp)
- {
- int int_types, int_count, int_actual, int_avail;
- int i, rc;
- if ((rc = ddi_intr_get_supported_types(pcnp->pcn_dip, &int_types)) !=
- DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to get supported interrupt "
- "types");
- return (DDI_FAILURE);
- }
- if ((int_types & DDI_INTR_TYPE_FIXED) == 0) {
- pcn_error(pcnp->pcn_dip, "fixed interrupts are not supported");
- return (DDI_FAILURE);
- }
- if ((rc = ddi_intr_get_nintrs(pcnp->pcn_dip, DDI_INTR_TYPE_FIXED,
- &int_count)) != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to obtain the number of "
- "interrupts");
- return (DDI_FAILURE);
- }
- if (int_count != 1) {
- pcn_error(pcnp->pcn_dip, "interrupt count is %d, expected 1",
- int_count);
- return (DDI_FAILURE);
- }
- rc = ddi_intr_get_navail(pcnp->pcn_dip, DDI_INTR_TYPE_FIXED, &int_avail);
- if ((rc != DDI_SUCCESS) || (int_avail == 0)) {
- pcn_error(pcnp->pcn_dip, "ddi_intr_get_avail failed");
- return (DDI_FAILURE);
- }
- rc = ddi_intr_alloc(pcnp->pcn_dip, &pcnp->pcn_htable, DDI_INTR_TYPE_FIXED, 0,
- int_count, &int_actual, DDI_INTR_ALLOC_STRICT);
- if ((rc != DDI_SUCCESS) || (int_actual != int_count)) {
- pcn_error(pcnp->pcn_dip, "unable to allocate fixed interrupt!");
- return (DDI_FAILURE);
- }
- if ((rc = ddi_intr_get_pri(pcnp->pcn_htable, &pcnp->pcn_int_pri)) !=
- DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to obtain interrupt priority");
- ddi_intr_free(pcnp->pcn_htable);
- return (DDI_FAILURE);
- }
- return (DDI_SUCCESS);
- }
- int
- pcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
- {
- pcn_t *pcnp;
- pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
- if (pcnp == NULL) {
- pcn_error(dip, "no soft state in detach!");
- return (DDI_FAILURE);
- }
- switch (cmd) {
- case DDI_DETACH:
- if (mac_unregister(pcnp->pcn_mh) != 0)
- return (DDI_FAILURE);
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcnp->pcn_flags &= ~PCN_RUNNING;
- pcn_stopall(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- ddi_intr_remove_handler(pcnp->pcn_htable);
- ddi_intr_free(pcnp->pcn_htable);
- mii_free(pcnp->pcn_mii);
- ddi_prop_remove_all(dip);
- pcn_freerxring(pcnp);
- pcn_freetxring(pcnp);
- ddi_regs_map_free(&pcnp->pcn_regshandle);
- mutex_destroy(&pcnp->pcn_intrlock);
- mutex_destroy(&pcnp->pcn_xmtlock);
- ddi_soft_state_free(pcn_ssp, ddi_get_instance(dip));
- return (DDI_SUCCESS);
- case DDI_SUSPEND:
- mii_suspend(pcnp->pcn_mii);
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcnp->pcn_flags |= PCN_SUSPENDED;
- pcn_stopall(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- return (DDI_SUCCESS);
- default:
- return (DDI_FAILURE);
- }
- }
- int
- pcn_resume(dev_info_t *dip)
- {
- pcn_t *pcnp;
- pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
- if (pcnp == NULL)
- return (DDI_FAILURE);
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcnp->pcn_flags &= ~PCN_SUSPENDED;
- if (!pcn_initialize(pcnp)) {
- pcn_error(pcnp->pcn_dip, "unable to resume chip");
- pcnp->pcn_flags |= PCN_SUSPENDED;
- mutex_exit(&pcnp->pcn_intrlock);
- mutex_exit(&pcnp->pcn_xmtlock);
- return (DDI_SUCCESS);
- }
- if (pcnp->pcn_flags & PCN_RUNNING)
- pcn_startall(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- mii_resume(pcnp->pcn_mii);
- return (DDI_SUCCESS);
- }
- int
- pcn_quiesce(dev_info_t *dip)
- {
- pcn_t *pcnp;
- pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
- if (pcnp == NULL)
- return (DDI_FAILURE);
- /*
- * First suspend the chip, this should drain anything in
- * in progress.
- */
- PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
- /* wait a reasonable amount of time for suspend to finish */
- drv_usecwait(RESET_DELAY);
- /* abrupt stop */
- PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP);
- return (DDI_SUCCESS);
- }
- static void
- pcn_setrxfilt(pcn_t *pcnp)
- {
- int i;
- if (pcnp->pcn_flags & PCN_SUSPENDED)
- return;
- /* temporairly suspend card */
- PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
- /* set promiscuous mode */
- if (pcnp->pcn_promisc)
- PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
- else
- PCN_CSR_CLRBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
- /* program mac address */
- pcn_csr_write(pcnp, PCN_CSR_PAR0,
- pcnp->pcn_addr[0] << 8 | pcnp->pcn_addr[1]);
- pcn_csr_write(pcnp, PCN_CSR_PAR1,
- pcnp->pcn_addr[2] << 8 | pcnp->pcn_addr[2]);
- pcn_csr_write(pcnp, PCN_CSR_PAR2,
- pcnp->pcn_addr[4] << 8 | pcnp->pcn_addr[5]);
- /*
- * program multicast filter
- * I'm not sure if this is absolutely necessary, but it appears that
- * the freebsd driver sets the filter to all 1's when in promiscuous
- * mode, so we follow suit.
- */
- for (i = 0; i < 4; i++)
- pcn_csr_write(pcnp, PCN_CSR_MAR0 + i,
- pcnp->pcn_promisc ? 0xffff : pcnp->pcn_mctab[i]);
- /* reenable card */
- PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
- }
- static int
- pcn_m_multicast(void *arg, boolean_t add, const uint8_t *macaddr)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- int index;
- uint32_t crc;
- uint32_t bit;
- uint32_t newval, oldval;
- /*
- * PCNet uses the upper 6 bits of the CRC of the macaddr
- * to index into a 64bit mask
- */
- CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
- crc = crc >> 26;
- index = crc / 16;
- bit = (1 << (crc % 16));
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- newval = oldval = pcnp->pcn_mctab[index];
- if (add) {
- pcnp->pcn_mccount[crc]++;
- if (pcnp->pcn_mccount[crc] == 1)
- newval |= bit;
- } else {
- pcnp->pcn_mccount[crc]--;
- if (pcnp->pcn_mccount[crc] == 0)
- newval &= ~bit;
- }
- if (newval != oldval) {
- pcnp->pcn_mctab[index] = newval;
- pcn_setrxfilt(pcnp);
- }
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- return (0);
- }
- static int
- pcn_m_promisc(void *arg, boolean_t on)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcnp->pcn_promisc = on;
- pcn_setrxfilt(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- return (0);
- }
- static int
- pcn_m_unicast(void *arg, const uint8_t *macaddr)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL);
- pcn_setrxfilt(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- return (0);
- }
- static mblk_t *
- pcn_m_tx(void *arg, mblk_t *mp)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mblk_t *nmp;
- mutex_enter(&pcnp->pcn_xmtlock);
- if (pcnp->pcn_flags & PCN_SUSPENDED) {
- while ((nmp = mp) != NULL) {
- pcnp->pcn_carrier_errors++;
- mp = mp->b_next;
- freemsg(nmp);
- }
- mutex_exit(&pcnp->pcn_xmtlock);
- return (NULL);
- }
- while (mp != NULL) {
- nmp = mp->b_next;
- mp->b_next = NULL;
- if (!pcn_send(pcnp, mp)) {
- mp->b_next = nmp;
- break;
- }
- mp = nmp;
- }
- mutex_exit(&pcnp->pcn_xmtlock);
- return (mp);
- }
- static boolean_t
- pcn_send(pcn_t *pcnp, mblk_t *mp)
- {
- size_t len;
- pcn_buf_t *txb;
- pcn_tx_desc_t *tmd;
- int txsend;
- ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
- ASSERT(mp != NULL);
- len = msgsize(mp);
- if (len > ETHERVLANMTU) {
- pcnp->pcn_macxmt_errors++;
- freemsg(mp);
- return (B_TRUE);
- }
- if (pcnp->pcn_txavail < PCN_TXRECLAIM)
- pcn_reclaim(pcnp);
- if (pcnp->pcn_txavail == 0) {
- pcnp->pcn_wantw = B_TRUE;
- pcn_enableinterrupts(pcnp);
- return (B_FALSE);
- }
- txsend = pcnp->pcn_txsend;
- txb = pcnp->pcn_txbufs[txsend];
- mcopymsg(mp, txb->pb_buf); /* frees mp! */
- pcnp->pcn_opackets++;
- pcnp->pcn_obytes += len;
- if (txb->pb_buf[0] & 0x1) {
- if (bcmp(txb->pb_buf, pcn_broadcast, ETHERADDRL) != 0)
- pcnp->pcn_multixmt++;
- else
- pcnp->pcn_brdcstxmt++;
- }
- SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV);
- tmd = &pcnp->pcn_txdescp[txsend];
- tmd->pcn_tbaddr = txb->pb_paddr;
- tmd->pcn_txctl = PCN_TXCTL_STP|PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|
- PCN_TXCTL_MORE_LTINT|0xf000;
- /* PCNet wants the 2's complement of the length of the buffer */
- tmd->pcn_txctl |= (~(len) + 1) & PCN_TXCTL_BUFSZ;
- /* Spec suggests this should be set last */
- tmd->pcn_txctl |= PCN_TXCTL_OWN;
- SYNCTXDESC(pcnp, txsend, DDI_DMA_SYNC_FORDEV);
- pcnp->pcn_txavail--;
- pcnp->pcn_txsend = (txsend + 1) % PCN_TXRING;
- pcnp->pcn_txstall_time = gethrtime() + (5 * 1000000000ULL);
- pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);
- return (B_TRUE);
- }
- static void
- pcn_reclaim(pcn_t *pcnp)
- {
- pcn_tx_desc_t *tmdp;
- while (pcnp->pcn_txavail != PCN_TXRING) {
- int index = pcnp->pcn_txreclaim;
- tmdp = &pcnp->pcn_txdescp[index];
- /* sync before reading */
- SYNCTXDESC(pcnp, index, DDI_DMA_SYNC_FORKERNEL);
- /* check if chip is still working on it */
- if (tmdp->pcn_txctl & PCN_TXCTL_OWN)
- break;
- pcnp->pcn_txavail++;
- pcnp->pcn_txreclaim = (index + 1) % PCN_TXRING;
- }
- if (pcnp->pcn_txavail >= PCN_TXRESCHED)
- if (pcnp->pcn_wantw) {
- pcnp->pcn_wantw = B_FALSE;
- pcn_enableinterrupts(pcnp);
- mac_tx_update(pcnp->pcn_mh);
- }
- }
- static unsigned
- pcn_intr(caddr_t arg1, caddr_t arg2)
- {
- pcn_t *pcnp = (pcn_t *)arg1;
- mblk_t *mp = NULL;
- uint32_t status;
- boolean_t do_reset = B_FALSE;
- mutex_enter(&pcnp->pcn_intrlock);
- if (pcnp->pcn_flags & PCN_SUSPENDED) {
- mutex_exit(&pcnp->pcn_intrlock);
- return (DDI_INTR_UNCLAIMED);
- }
- CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_CSR);
- while ((status = CSR_READ_4(pcnp, PCN_IO32_RDP)) & PCN_CSR_INTR) {
- CSR_WRITE_4(pcnp, PCN_IO32_RDP, status);
- if (status & PCN_CSR_TINT) {
- mutex_enter(&pcnp->pcn_xmtlock);
- pcn_reclaim(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- }
- if (status & PCN_CSR_RINT)
- mp = pcn_receive(pcnp);
- if (status & PCN_CSR_ERR) {
- do_reset = B_TRUE;
- break;
- }
- }
- if (do_reset) {
- mutex_enter(&pcnp->pcn_xmtlock);
- pcn_resetall(pcnp);
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- mii_reset(pcnp->pcn_mii);
- } else {
- mutex_exit(&pcnp->pcn_intrlock);
- }
- if (mp)
- mac_rx(pcnp->pcn_mh, NULL, mp);
- return (DDI_INTR_CLAIMED);
- }
- static mblk_t *
- pcn_receive(pcn_t *pcnp)
- {
- uint32_t len;
- pcn_buf_t *rxb;
- pcn_rx_desc_t *rmd;
- mblk_t *mpchain, **mpp, *mp;
- int head, cnt;
- mpchain = NULL;
- mpp = &mpchain;
- head = pcnp->pcn_rxhead;
- for (cnt = 0; cnt < PCN_RXRING; cnt++) {
- rmd = &pcnp->pcn_rxdescp[head];
- rxb = pcnp->pcn_rxbufs[head];
- SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORKERNEL);
- if (rmd->pcn_rxstat & PCN_RXSTAT_OWN)
- break;
- len = rmd->pcn_rxlen - ETHERFCSL;
- if (rmd->pcn_rxstat & PCN_RXSTAT_ERR) {
- pcnp->pcn_errrcv++;
- if (rmd->pcn_rxstat & PCN_RXSTAT_FRAM)
- pcnp->pcn_align_errors++;
- if (rmd->pcn_rxstat & PCN_RXSTAT_OFLOW)
- pcnp->pcn_overflow++;
- if (rmd->pcn_rxstat & PCN_RXSTAT_CRC)
- pcnp->pcn_fcs_errors++;
- } else if (len > ETHERVLANMTU) {
- pcnp->pcn_errrcv++;
- pcnp->pcn_toolong_errors++;
- } else {
- mp = allocb(len + PCN_HEADROOM, 0);
- if (mp == NULL) {
- pcnp->pcn_errrcv++;
- pcnp->pcn_norcvbuf++;
- goto skip;
- }
- SYNCBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
- mp->b_rptr += PCN_HEADROOM;
- mp->b_wptr = mp->b_rptr + len;
- bcopy((char *)rxb->pb_buf, mp->b_rptr, len);
- pcnp->pcn_ipackets++;
- pcnp->pcn_rbytes++;
- if (rmd->pcn_rxstat & PCN_RXSTAT_LAFM|PCN_RXSTAT_BAM) {
- if (rmd->pcn_rxstat & PCN_RXSTAT_BAM)
- pcnp->pcn_brdcstrcv++;
- else
- pcnp->pcn_multircv++;
- }
- *mpp = mp;
- mpp = &mp->b_next;
- }
- skip:
- rmd->pcn_rxstat &= ~(PCN_RXSTAT_OWN);
- SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORDEV);
- head = (head + 1) % PCN_RXRING;
- }
- pcnp->pcn_rxhead = head;
- return (mpchain);
- }
- static void
- pcn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- if (mii_m_loop_ioctl(pcnp->pcn_mii, wq, mp))
- return;
- miocnak(wq, mp, 0, EINVAL);
- }
- static int
- pcn_m_start(void *arg)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcn_startall(pcnp);
- pcnp->pcn_flags |= PCN_RUNNING;
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- mii_start(pcnp->pcn_mii);
- return (0);
- }
- static void
- pcn_m_stop(void *arg)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mii_stop(pcnp->pcn_mii);
- mutex_enter(&pcnp->pcn_intrlock);
- mutex_enter(&pcnp->pcn_xmtlock);
- pcn_stopall(pcnp);
- pcnp->pcn_flags &= ~PCN_RUNNING;
- mutex_exit(&pcnp->pcn_xmtlock);
- mutex_exit(&pcnp->pcn_intrlock);
- }
- static int
- pcn_initialize(pcn_t *pcnp)
- {
- uint32_t par;
- /*
- * Issue a reset by reading from the RESET register.
- * Note that we don't know if the chip is operating in
- * 16-bit or 32-bit mode at this point, so we attempt
- * to reset the chip both ways. If one fails, the other
- * will succeed.
- */
- CSR_READ_2(pcnp, PCN_IO16_RESET);
- CSR_READ_4(pcnp, PCN_IO32_RESET);
- drv_usecwait(1000);
- /* Select 32-bit (DWIO) mode */
- CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0);
- /* Select software style 3. */
- pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST);
- par = 0;
-
- pcn_resetrings(pcnp);
- pcn_setrxfilt(pcnp);
- /* Enable fast suspend mode. */
- PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);
- return (DDI_SUCCESS);
- }
- static void
- pcn_resetall(pcn_t *pcnp)
- {
- pcn_stopall(pcnp);
- pcn_startall(pcnp);
- }
- static void
- pcn_startmac(pcn_t *pcnp)
- {
- ASSERT(mutex_owned(&pcnp->pcn_intrlock));
- ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
- PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_START);
- if (pcnp->pcn_flags & PCN_RUNNING)
- mac_tx_update(pcnp->pcn_mh);
- }
- static void
- pcn_stopmac(pcn_t *pcnp)
- {
- ASSERT(mutex_owned(&pcnp->pcn_intrlock));
- ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
- PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP);
- }
- static void
- pcn_startall(pcn_t *pcnp)
- {
- ASSERT(mutex_owned(&pcnp->pcn_intrlock));
- ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
- pcn_disableinterrupts(pcnp);
- (void) pcn_initialize(pcnp);
- pcn_enableinterrupts(pcnp);
- pcn_startmac(pcnp);
- }
- static void
- pcn_stopall(pcn_t *pcnp)
- {
- pcn_disableinterrupts(pcnp);
- pcn_stopmac(pcnp);
- }
- static int
- pcn_m_stat(void *arg, uint_t stat, uint64_t *val)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- if (mii_m_getstat(pcnp->pcn_mii, stat, val) == 0)
- return (0);
- switch (stat) {
- case MAC_STAT_MULTIRCV:
- *val = pcnp->pcn_multircv;
- break;
- case MAC_STAT_BRDCSTRCV:
- *val = pcnp->pcn_brdcstrcv;
- break;
- case MAC_STAT_MULTIXMT:
- *val = pcnp->pcn_multixmt;
- break;
- case MAC_STAT_BRDCSTXMT:
- *val = pcnp->pcn_brdcstxmt;
- break;
- case MAC_STAT_IPACKETS:
- *val = pcnp->pcn_ipackets;
- break;
- case MAC_STAT_RBYTES:
- *val = pcnp->pcn_rbytes;
- break;
- case MAC_STAT_OPACKETS:
- *val = pcnp->pcn_opackets;
- break;
- case MAC_STAT_OBYTES:
- *val = pcnp->pcn_obytes;
- break;
- case MAC_STAT_NORCVBUF:
- *val = pcnp->pcn_norcvbuf;
- break;
- case MAC_STAT_NOXMTBUF:
- *val = 0;
- break;
- case MAC_STAT_COLLISIONS:
- *val = pcnp->pcn_collisions;
- break;
- case MAC_STAT_IERRORS:
- *val = pcnp->pcn_errrcv;
- break;
- case MAC_STAT_OERRORS:
- *val = pcnp->pcn_errxmt;
- break;
- case ETHER_STAT_ALIGN_ERRORS:
- *val = pcnp->pcn_align_errors;
- break;
- case ETHER_STAT_FCS_ERRORS:
- *val = pcnp->pcn_fcs_errors;
- break;
- case ETHER_STAT_SQE_ERRORS:
- *val = pcnp->pcn_sqe_errors;
- break;
- case ETHER_STAT_DEFER_XMTS:
- *val = pcnp->pcn_defer_xmts;
- break;
- case ETHER_STAT_FIRST_COLLISIONS:
- *val = pcnp->pcn_first_collisions;
- break;
- case ETHER_STAT_MULTI_COLLISIONS:
- *val = pcnp->pcn_multi_collisions;
- break;
- case ETHER_STAT_TX_LATE_COLLISIONS:
- *val = pcnp->pcn_tx_late_collisions;
- break;
- case ETHER_STAT_EX_COLLISIONS:
- *val = pcnp->pcn_ex_collisions;
- break;
- case ETHER_STAT_MACXMT_ERRORS:
- *val = pcnp->pcn_macxmt_errors;
- break;
- case ETHER_STAT_CARRIER_ERRORS:
- *val = pcnp->pcn_carrier_errors;
- break;
- case ETHER_STAT_TOOLONG_ERRORS:
- *val = pcnp->pcn_toolong_errors;
- break;
- case ETHER_STAT_MACRCV_ERRORS:
- *val = pcnp->pcn_macrcv_errors;
- break;
- case MAC_STAT_OVERFLOWS:
- *val = pcnp->pcn_overflow;
- break;
- case MAC_STAT_UNDERFLOWS:
- *val = pcnp->pcn_underflow;
- break;
- case ETHER_STAT_TOOSHORT_ERRORS:
- *val = pcnp->pcn_runt;
- break;
- case ETHER_STAT_JABBER_ERRORS:
- *val = pcnp->pcn_jabber;
- break;
- default:
- return (ENOTSUP);
- }
- return (0);
- }
- static int
- pcn_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t flags,
- uint_t sz, void *val, uint_t *perm)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- return (mii_m_getprop(pcnp->pcn_mii, name, num, flags, sz, val, perm));
- }
- static int
- pcn_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
- const void *val)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- return (mii_m_setprop(pcnp->pcn_mii, name, num, sz, val));
- }
- static uint16_t
- pcn_mii_read(void *arg, uint8_t phy, uint8_t reg)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- uint16_t val;
- /*
- * At least Am79C971 with DP83840A wedge when isolating the
- * external PHY so we can't allow multiple external PHYs.
- * There are cards that use Am79C971 with both the internal
- * and an external PHY though.
- * For internal PHYs it doesn't really matter whether we can
- * isolate the remaining internal and the external ones in
- * the PHY drivers as the internal PHYs have to be enabled
- * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc.
- * With Am79C97{3,5,8} we don't support switching beetween
- * the internal and external PHYs, yet, so we can't allow
- * multiple PHYs with these either.
- * Am79C97{2,6} actually only support external PHYs (not
- * connectable internal ones respond at the usual addresses,
- * which don't hurt if we let them show up on the bus) and
- * isolating them works.
- */
- if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
- pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
- pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr != 0xff &&
- phy != pcnp->pcn_extphyaddr)
- return (0xffff);
- pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, reg | (phy << 5));
- val = pcn_bcr_read(pcnp, PCN_BCR_MIIDATA) & 0xFFFF;
- if (val == 0xFFFF)
- return (val);
- if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
- pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
- pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr == 0xff)
- pcnp->pcn_extphyaddr = phy;
- return (val);
- }
- static void
- pcn_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, reg | (phy << 5));
- pcn_bcr_write(pcnp, PCN_BCR_MIIDATA, val);
- }
- static void
- pcn_mii_notify(void *arg, link_state_t link)
- {
- pcn_t *pcnp = (pcn_t *)arg;
- mac_link_update(pcnp->pcn_mh, link);
- }
- static const pcn_type_t *
- pcn_match(uint16_t vid, uint16_t did)
- {
- const pcn_type_t *t;
- t = pcn_devs;
- while (t->pcn_name != NULL) {
- if ((vid == t->pcn_vid) && (did == t->pcn_did))
- return (t);
- t++;
- }
- return (NULL);
- }
- static char *
- pcn_chipid_name(uint32_t id)
- {
- const struct pcn_chipid *p;
- p = pcn_chipid;
- while (p->name) {
- if (id == p->id)
- return (p->name);
- p++;
- }
- return ("Unknown");
- }
- static void
- pcn_getfactaddr(pcn_t *pcnp)
- {
- uint32_t addr[2];
- addr[0] = CSR_READ_4(pcnp, PCN_IO32_APROM00);
- addr[1] = CSR_READ_4(pcnp, PCN_IO32_APROM01);
- pcnp->pcn_addr[5] = addr[1] >> 8 & 0x000000ffL;
- pcnp->pcn_addr[4] = addr[1] & 0x000000ffL;
- pcnp->pcn_addr[3] = addr[0] >> 24;
- pcnp->pcn_addr[2] = addr[0] >> 16 & 0x000000ffL;
- pcnp->pcn_addr[1] = addr[0] >> 8 & 0x000000ffL;
- pcnp->pcn_addr[0] = addr[0] & 0x000000ffL;
- }
- static void
- pcn_enableinterrupts(pcn_t *pcnp)
- {
- pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_INTEN);
- }
- static void
- pcn_disableinterrupts(pcn_t *pcnp)
- {
- PCN_CSR_CLRBIT(pcnp, PCN_CSR_CSR, PCN_CSR_INTEN);
- }
- static uint32_t
- pcn_csr_read(pcn_t *pcnp, uint32_t reg)
- {
- CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
- return (CSR_READ_4(pcnp, PCN_IO32_RDP));
- }
- static uint16_t
- pcn_csr_read16(pcn_t *pcnp, uint32_t reg)
- {
- CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
- return (CSR_READ_2(pcnp, PCN_IO16_RDP));
- }
- static void
- pcn_csr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
- {
- CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
- CSR_WRITE_4(pcnp, PCN_IO32_RDP, val);
- }
- static uint32_t
- pcn_bcr_read(pcn_t *pcnp, uint32_t reg)
- {
- CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
- return (CSR_READ_4(pcnp, PCN_IO32_BDP));
- }
- static uint16_t
- pcn_bcr_read16(pcn_t *pcnp, uint32_t reg)
- {
- CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
- return (CSR_READ_2(pcnp, PCN_IO16_BDP));
- }
- static void
- pcn_bcr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
- {
- CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
- CSR_WRITE_4(pcnp, PCN_IO32_BDP, val);
- }
- static void
- pcn_resetrings(pcn_t *pcnp)
- {
- int i;
- pcnp->pcn_rxhead = 0;
- pcnp->pcn_txreclaim = 0;
- pcnp->pcn_txsend = 0;
- pcnp->pcn_txavail = PCN_TXRING;
- /* set addresses of decriptors */
- pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
- pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
- (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);
- pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
- pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
- (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);
- /* set the ring sizes */
- pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
- pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
- /* don't use the initializtation block */
- pcn_csr_write(pcnp, PCN_CSR_IAB1, 0);
- /* reset rx descriptor values */
- for (i = 0; i < PCN_RXRING; i++) {
- pcn_rx_desc_t *rmd = &pcnp->pcn_rxdescp[i];
- pcn_buf_t *rxb = pcnp->pcn_rxbufs[i];
- rmd->pcn_rbaddr = rxb->pb_paddr;
- rmd->pcn_bufsz = (~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ;
- rmd->pcn_bufsz |= PCN_RXLEN_MBO;
- rmd->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN;
- }
- /* tx descriptors get set on each tx, so no need to do it again */
- }
- static void
- pcn_destroybuf(pcn_buf_t *buf)
- {
- if (buf == NULL)
- return;
- if (buf->pb_paddr)
- (void) ddi_dma_unbind_handle(buf->pb_dmah);
- if (buf->pb_acch)
- ddi_dma_mem_free(&buf->pb_acch);
- if (buf->pb_dmah)
- ddi_dma_free_handle(&buf->pb_dmah);
- kmem_free(buf, sizeof (*buf));
- }
- static pcn_buf_t *
- pcn_allocbuf(pcn_t *pcnp)
- {
- pcn_buf_t *buf;
- size_t len;
- unsigned ccnt;
- ddi_dma_cookie_t dmac;
- buf = kmem_zalloc(sizeof (*buf), KM_SLEEP);
- if (ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
- NULL, &buf->pb_dmah) != DDI_SUCCESS) {
- kmem_free(buf, sizeof (*buf));
- return (NULL);
- }
- if (ddi_dma_mem_alloc(buf->pb_dmah, PCN_BUFSZ, &pcn_bufattr,
- DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->pb_buf, &len,
- &buf->pb_acch) != DDI_SUCCESS) {
- pcn_destroybuf(buf);
- return (NULL);
- }
- if (ddi_dma_addr_bind_handle(buf->pb_dmah, NULL, buf->pb_buf, len,
- DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
- &ccnt) != DDI_DMA_MAPPED) {
- pcn_destroybuf(buf);
- return (NULL);
- }
- buf->pb_paddr = dmac.dmac_address;
- return (buf);
- }
- static int
- pcn_alloctxring(pcn_t *pcnp)
- {
- int rval;
- int i;
- size_t size;
- size_t len;
- ddi_dma_cookie_t dmac;
- unsigned ncookies;
- caddr_t kaddr;
- size = PCN_TXRING * sizeof (pcn_tx_desc_t);
- rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
- NULL, &pcnp->pcn_txdesc_dmah);
- if (rval != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for tx "
- "descriptors");
- return (DDI_FAILURE);
- }
- rval = ddi_dma_mem_alloc(pcnp->pcn_txdesc_dmah, size, &pcn_devattr,
- DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
- &pcnp->pcn_txdesc_acch);
- if (rval != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for tx "
- "descriptors");
- return (DDI_FAILURE);
- }
- rval = ddi_dma_addr_bind_handle(pcnp->pcn_txdesc_dmah, NULL, kaddr,
- size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
- &ncookies);
- if (rval != DDI_DMA_MAPPED) {
- pcn_error(pcnp->pcn_dip, "unable to bind DMA for tx "
- "descriptors");
- return (DDI_FAILURE);
- }
- ASSERT(ncookies == 1);
- pcnp->pcn_txdesc_paddr = dmac.dmac_address;
- pcnp->pcn_txdescp = (void *)kaddr;
- pcnp->pcn_txbufs = kmem_zalloc(PCN_TXRING * sizeof (pcn_buf_t *),
- KM_SLEEP);
- for (i = 0; i < PCN_TXRING; i++) {
- pcn_buf_t *txb = pcn_allocbuf(pcnp);
- if (txb == NULL)
- return (DDI_FAILURE);
- pcnp->pcn_txbufs[i] = txb;
- }
- return (DDI_SUCCESS);
- }
- static int
- pcn_allocrxring(pcn_t *pcnp)
- {
- int rval;
- int i;
- size_t len;
- size_t size;
- ddi_dma_cookie_t dmac;
- unsigned ncookies;
- caddr_t kaddr;
- size = PCN_RXRING * sizeof (pcn_rx_desc_t);
- rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dmadesc_attr,
- DDI_DMA_SLEEP, NULL, &pcnp->pcn_rxdesc_dmah);
- if (rval != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for rx "
- "descriptors");
- return (DDI_FAILURE);
- }
- rval = ddi_dma_mem_alloc(pcnp->pcn_rxdesc_dmah, size, &pcn_devattr,
- DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
- &pcnp->pcn_rxdesc_acch);
- if (rval != DDI_SUCCESS) {
- pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for rx "
- "descriptors");
- return (DDI_FAILURE);
- }
- rval = ddi_dma_addr_bind_handle(pcnp->pcn_rxdesc_dmah, NULL, kaddr,
- size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
- &ncookies);
- if (rval != DDI_DMA_MAPPED) {
- pcn_error(pcnp->pcn_dip, "unable to bind DMA for rx "
- "descriptors");
- return (DDI_FAILURE);
- }
- ASSERT(ncookies == 1);
- pcnp->pcn_rxdesc_paddr = dmac.dmac_address;
- pcnp->pcn_rxdescp = (void *)kaddr;
- pcnp->pcn_rxbufs = kmem_zalloc(PCN_RXRING * sizeof (pcn_buf_t *),
- KM_SLEEP);
- for (i = 0; i < PCN_RXRING; i++) {
- pcn_buf_t *rxb = pcn_allocbuf(pcnp);
- if (rxb == NULL)
- return (DDI_FAILURE);
- pcnp->pcn_rxbufs[i] = rxb;
- }
- return (DDI_SUCCESS);
- }
- static void
- pcn_freetxring(pcn_t *pcnp)
- {
- int i;
- for (i = 0; i < PCN_TXRING; i++)
- pcn_destroybuf(pcnp->pcn_txbufs[i]);
- if (pcnp->pcn_txbufs)
- kmem_free(pcnp->pcn_txbufs, PCN_TXRING * sizeof (pcn_buf_t *));
- if (pcnp->pcn_txdesc_paddr)
- (void) ddi_dma_unbind_handle(pcnp->pcn_txdesc_dmah);
- if (pcnp->pcn_txdesc_acch)
- ddi_dma_mem_free(&pcnp->pcn_txdesc_acch);
- if (pcnp->pcn_txdesc_dmah)
- ddi_dma_free_handle(&pcnp->pcn_txdesc_dmah);
- }
- static void
- pcn_freerxring(pcn_t *pcnp)
- {
- int i;
- for (i = 0; i < PCN_RXRING; i++)
- pcn_destroybuf(pcnp->pcn_rxbufs[i]);
- if (pcnp->pcn_rxbufs)
- kmem_free(pcnp->pcn_rxbufs, PCN_RXRING * sizeof (pcn_buf_t *));
- if (pcnp->pcn_rxdesc_paddr)
- (void) ddi_dma_unbind_handle(pcnp->pcn_rxdesc_dmah);
- if (pcnp->pcn_rxdesc_acch)
- ddi_dma_mem_free(&pcnp->pcn_rxdesc_acch);
- if (pcnp->pcn_rxdesc_dmah)
- ddi_dma_free_handle(&pcnp->pcn_rxdesc_dmah);
- }
- static void
- pcn_error(dev_info_t *dip, char *fmt, ...)
- {
- va_list ap;
- char buf[256];
- va_start(ap, fmt);
- (void) vsnprintf(buf, sizeof (buf), fmt, ap);
- va_end(ap);
- if (dip)
- cmn_err(CE_WARN, "%s%d: %s", ddi_driver_name(dip),
- ddi_get_instance(dip), buf);
- else
- cmn_err(CE_WARN, "pcn: %s", buf);
- }