Mercurial > illumos > illumos-gate
changeset 13465:8a44db59c6f4
19 Provide an opensource version of pcn(7D)
Reviewed by: Garret D'Amore <garrett@nexenta.com>
Reviewed by: Rich Lowe <richlowe@richlowe.net>
Approved by: Albert Lee <trisk@nexenta.com>
author | Jason King <jason.brian.king+illumoshg@gmail.com> |
---|---|
date | Mon, 26 Sep 2011 20:45:52 -0400 |
parents | e73ffd53b1d6 |
children | e23b8d796527 |
files | exception_lists/closed-bins usr/src/man/man7d/Makefile usr/src/man/man7d/pcn.7d usr/src/pkg/manifests/driver-network-pcn.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/pcn/THIRDPARTYLICENSE usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip usr/src/uts/common/io/pcn/pcn.c usr/src/uts/common/io/pcn/pcn.h usr/src/uts/common/io/pcn/pcnimpl.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/pcn/Makefile |
diffstat | 13 files changed, 2900 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/exception_lists/closed-bins Thu Sep 22 13:37:45 2011 +0400 +++ b/exception_lists/closed-bins Mon Sep 26 20:45:52 2011 -0400 @@ -15,6 +15,8 @@ ./etc/snmp/conf/snmp.conf ./kernel/drv/iprb ./kernel/drv/amd64/iprb +./kernel/drv/pcn +./kernel/drv/amd64/pcn ./lib/crypto ./lib/crypto/kcfd ./lib/libc_i18n.a
--- a/usr/src/man/man7d/Makefile Thu Sep 22 13:37:45 2011 +0400 +++ b/usr/src/man/man7d/Makefile Mon Sep 26 20:45:52 2011 -0400 @@ -93,6 +93,7 @@ pcata.7d \ pcic.7d \ pcmcia.7d \ + pcn.7d \ pcwl.7d \ physmem.7d \ pm.7d \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/man/man7d/pcn.7d Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,181 @@ +'\" te +.\" Copyright 2011 Jason King. All rights reserved. +.\" Copyright (c) 2001-2007 by Garrett D'Amore. +.\" 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. 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 THE COPYRIGHT HOLDER 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 THE COPYRIGHT HOLDER OR +.\" CONTRIBUTORS 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 +.\" Portions Copyright (c) 2007 by Sun Microsystems, Inc. All Rights Reserved. + +.TH "PCN" "7D" "Sep 16, 2011" +. +.SH "NAME" +\fBpcn\fR \- PCnet Ethernet device driver +.SH "SYNOPSIS" +.LP +.nf +\fB/dev/pcn\fR +.fi + +.SH "DESCRIPTION" +.sp +.LP +The \fBpcn\fR driver is a multi\-threaded, loadable, clonable GLDv3\-based +STREAMS driver supporting the Data Link Provider Interface \fBdlpi\fR(7P) for +the AMD PCnet family of Ethernet controllers\. +.SH "APPLICATION PROGRAMMING INTERFACE" +The \fBpcn\fR driver can be used as either a "style 1" or a "style 2" Data Link +Service Provider\. Physical points of attachment (PPAs) are interpreted as the +instance number of the \fBpcn\fR controller as assigned by the Illumos +operating environment\. +.sp +.LP +The values returned by the driver in the \fBDL_INFO_ACK\fR response are: +.RS +4 +.TP +.ie t \(bu +.el o +Maximum SDU is 1500\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +Minimum SDU is 0\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +The dlsap address length is 8\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +MAC type is \fBDL_ETHER\fR\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +SAP length is \-2\. The 6\-byte physical address is immediately followed by a +2\-byte SAP\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +Service mode is \fBDL_CLDLS\fR\. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +The broadcast address is the 6\-byte Ethernet broadcast address +(\fBff:ff:ff:ff:ff:ff\fR)\. +.SH "CONFIGURATION" +.sp +.LP +The \fBpcn\fR driver performs auto-negotiation to select the link speed and +mode\. Link sped may be 100Mbps full\-duplex, 100Mbps half\-duplex, +10Mbps full\-duplex, or 10Mbps half\-duplex, depending on the hardware +adapter type\. See the \fIIEEE802.3\fR standard for more information\. +.sp +.LP +The capabilities advertised by the \fBpcn\fR device can be set using +\fBdladm\fR(1m)\. The driver supports a number of parameters whose names +being with \fBen_\fR (see below)\. Each of these parameters contains a +boolean value that determines if the devices advertises that mode of +operations\. The \fBadv_autoneg_cap\fR parameter controls whether +auto-negotioation is performed\. If \fBadv_autoneg_cap\fR is set to 0, the +driver forces the mode of operation selected by the first non-zero +parameter in priority order as shown below: +.sp +.in +2 +.nf + (highest priority/greatest throughput) + en_100fdx_cap 100Mpbs full duplex + en_10fdx_cap 10Mpbs full duplex + (lowest priority/least throughput) +.fi +.in -2 + +.sp +.LP +All capabilities default to enabled\. Note that changing any capability +parameter causes te link to go down while the link partners renegotiate +the link speed/duplex using the newly changed capabilities\. +.SH "ATTRIBUTES" +.sp +.LP +See \fBattributes\fR(5) for a description of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Architecture x86 +_ +Interface Stability Committed +.TE + +.SH "FILES" +.sp +.ne 2 +.na +\fB\fB/dev/pcn\fR\fR +.ad +.sp .6 +.RS 4n +Special character device\. +.RE + +.sp +.ne 2 +.na +\fB\fB/kernel/drv/pcn\fR\fR +.ad +.sp 6 +.RS 4n +32\-bit driver binary\. +.RE + +.sp +.ne 2 +.na +\fB\fB/kernel/drv/amd64/pcn\fR\fR +.ad +.sp .6 +.RS 4n +64\-bit driver binary (x86)\. +.RE + +.SH "SEE ALSO" +.sp +.LP +\fBdlpi\fR(1m), \fBattributes\fR(5), \fBstreamio\fR(7I), \fBdlpi\fR(7p) +.sp +.LP +\fIIEEE 802.3\fR \(em Institute of Electrical and Electronics Engineers, 2002
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkg/manifests/driver-network-pcn.mf Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2011 Jason King. All rights reserved. + +# +# The default for payload-bearing actions in this package is to appear in the +# global zone only. See the include file for greater detail, as well as +# information about overriding the defaults. +# +<include global_zone_only_component> +set name=pkg.fmri value=pkg:/driver/network/pcn@$(PKGVERS) +set name=pkg.description value="PCnet Fast Ethernet Network Adapter Driver" +set name=pkg.summary value="PCnet Ethernet Driver" +set name=info.classification \ + value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=$(ARCH) +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +dir path=usr/share/man +dir path=usr/share/man/man7d +driver name=pcn clone_perms="pcn 0666 root sys" perms="* 0666 root sys" \ + alias=pci1022,2000 \ + alias=pci103c,104c +file path=kernel/drv/$(ARCH64)/pcn group=sys +$(i386_ONLY)file path=kernel/drv/pcn group=sys +file path=usr/share/man/man7d/pcn.7d +license license_in_headers license=license_in_headers +license usr/src/uts/common/io/pcn/THIRDPARTYLICENSE \ + license=usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
--- a/usr/src/uts/common/Makefile.files Thu Sep 22 13:37:45 2011 +0400 +++ b/usr/src/uts/common/Makefile.files Mon Sep 26 20:45:52 2011 -0400 @@ -1780,6 +1780,8 @@ NGE_OBJS += nge_main.o nge_atomic.o nge_chip.o nge_ndd.o nge_kstats.o \ nge_log.o nge_rx.o nge_tx.o nge_xmii.o +PCN_OBJS += pcn.o + RGE_OBJS += rge_main.o rge_chip.o rge_ndd.o rge_kstats.o rge_log.o rge_rxtx.o URTW_OBJS += urtw.o
--- a/usr/src/uts/common/Makefile.rules Thu Sep 22 13:37:45 2011 +0400 +++ b/usr/src/uts/common/Makefile.rules Mon Sep 26 20:45:52 2011 -0400 @@ -975,6 +975,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcn/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcwl/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -2245,6 +2249,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcan/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcn/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcwl/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,31 @@ +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. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,1 @@ +PCNET FAST ETHERNET DRIVER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pcn/pcn.c Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,1827 @@ +/* + * Copyright (c) 2011 Jason King. + * 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_ddi_resume(dev_info_t *); +static int pcn_quiesce(dev_info_t *); + +static void pcn_teardown(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, + void *); +static int pcn_m_setprop(void *, const char *, mac_prop_id_t, uint_t, + const void *); +static void pcn_m_propinfo(void *, const char *, mac_prop_id_t, + mac_prop_info_handle_t); +static int pcn_watchdog(pcn_t *); + +static unsigned pcn_intr(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 pcn_buf_t *pcn_allocbuf(pcn_t *); +static void pcn_destroybuf(pcn_buf_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 *, boolean_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_getfactaddr(pcn_t *); +static int pcn_set_chipid(pcn_t *, uint32_t); +static const pcn_type_t *pcn_match(uint16_t, uint16_t); +static void pcn_start_timer(pcn_t *); +static void pcn_stop_timer(pcn_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 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 | MC_PROPINFO, + pcn_m_stat, + pcn_m_start, + pcn_m_stop, + pcn_m_promisc, + pcn_m_multicast, + pcn_m_unicast, + pcn_m_tx, + NULL, + pcn_m_ioctl, + NULL, /* mc_getcapab */ + NULL, /* mc_open */ + NULL, /* mc_close */ + pcn_m_setprop, + pcn_m_getprop, + pcn_m_propinfo +}; + +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; + uint16_t venid; + uint16_t devid; + uint16_t svid; + uint16_t ssid; + + switch (cmd) { + case DDI_RESUME: + return (pcn_ddi_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); + } + + 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; + pcnp->pcn_extphyaddr = -1; + + if (ddi_get_iblock_cookie(dip, 0, &pcnp->pcn_icookie) != DDI_SUCCESS) { + pcn_error(pcnp->pcn_dip, "ddi_get_iblock_cookie failed"); + ddi_soft_state_free(pcn_ssp, instance); + pci_config_teardown(&pci); + return (DDI_FAILURE); + } + + + mutex_init(&pcnp->pcn_xmtlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie); + mutex_init(&pcnp->pcn_intrlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie); + mutex_init(&pcnp->pcn_reglock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie); + + /* + * 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 (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 ((pcnp->pcn_mii = mii_alloc(pcnp, dip, &pcn_mii_ops)) == NULL) + goto fail; + + /* XXX: need to set based on device */ + mii_set_pauseable(pcnp->pcn_mii, B_FALSE, B_FALSE); + + if ((pcn_allocrxring(pcnp) != DDI_SUCCESS) || + (pcn_alloctxring(pcnp) != DDI_SUCCESS)) { + pcn_error(dip, "unable to allocate DMA resources"); + goto fail; + } + + pcnp->pcn_promisc = B_FALSE; + + mutex_enter(&pcnp->pcn_intrlock); + mutex_enter(&pcnp->pcn_xmtlock); + rc = pcn_initialize(pcnp, B_TRUE); + mutex_exit(&pcnp->pcn_xmtlock); + mutex_exit(&pcnp->pcn_intrlock); + if (rc != DDI_SUCCESS) + goto fail; + + if (ddi_add_intr(dip, 0, NULL, NULL, pcn_intr, (caddr_t)pcnp) != + DDI_SUCCESS) { + pcn_error(dip, "unable to add interrupt"); + goto fail; + } + + pcnp->pcn_flags |= PCN_INTR_ENABLED; + + 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); + return (DDI_SUCCESS); + } + + mac_free(macp); + + return (DDI_SUCCESS); + +fail: + pcn_teardown(pcnp); + return (DDI_FAILURE); +} + +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); + + pcn_teardown(pcnp); + 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_ddi_resume(dev_info_t *dip) +{ + pcn_t *pcnp; + + if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL) + return (DDI_FAILURE); + + mutex_enter(&pcnp->pcn_intrlock); + mutex_enter(&pcnp->pcn_xmtlock); + + pcnp->pcn_flags &= ~PCN_SUSPENDED; + + if (!pcn_initialize(pcnp, B_FALSE)) { + 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 (IS_RUNNING(pcnp)) + 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; + + if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL) + return (DDI_FAILURE); + + /* don't want to take the chance of blocking */ + CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_EXTCTL1); + CSR_WRITE_4(pcnp, PCN_IO32_RDP, CSR_READ_4(pcnp, PCN_IO32_RDP) & + ~(PCN_EXTCTL1_SINTEN)); + + CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_CSR); + CSR_WRITE_4(pcnp, PCN_IO32_RDP, + (CSR_READ_4(pcnp, PCN_IO32_RDP) & ~(PCN_CSR_INTEN)) | + PCN_CSR_STOP); + + return (DDI_SUCCESS); +} + +static void +pcn_teardown(pcn_t *pcnp) +{ + ASSERT(!(pcnp->pcn_flags & PCN_RUNNING)); + + if (pcnp->pcn_mii != NULL) { + mii_free(pcnp->pcn_mii); + pcnp->pcn_mii = NULL; + } + + if (pcnp->pcn_flags & PCN_INTR_ENABLED) + ddi_remove_intr(pcnp->pcn_dip, 0, pcnp->pcn_icookie); + + /* These will exit gracefully if not yet allocated */ + pcn_freerxring(pcnp); + pcn_freetxring(pcnp); + + if (pcnp->pcn_regshandle != NULL) + ddi_regs_map_free(&pcnp->pcn_regshandle); + + + mutex_destroy(&pcnp->pcn_xmtlock); + mutex_destroy(&pcnp->pcn_intrlock); + mutex_destroy(&pcnp->pcn_reglock); + + ddi_soft_state_free(pcn_ssp, ddi_get_instance(pcnp->pcn_dip)); +} + +/* + * Drains any FIFOs in the card, then pauses it + */ +static void +pcn_suspend(pcn_t *pcnp) +{ + uint32_t val; + int i; + + PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + for (i = 0; i < 5000; i++) { + if ((val = pcn_csr_read(pcnp, PCN_CSR_EXTCTL1)) & + PCN_EXTCTL1_SPND) + return; + drv_usecwait(1000); + } + + pcn_error(pcnp->pcn_dip, "Unable to suspend, EXTCTL1 was 0x%b", val, + PCN_EXTCTL1_STR); +} + +static void +pcn_resume(pcn_t *pcnp) +{ + 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; + uint16_t bit; + uint16_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 >>= 26; + index = crc / 16; + bit = (1U << (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_suspend(pcnp); + pcn_csr_write(pcnp, PCN_CSR_MAR0 + index, newval); + pcn_resume(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; + + if (IS_RUNNING(pcnp)) + pcn_suspend(pcnp); + + /* 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); + + if (IS_RUNNING(pcnp)) + pcn_resume(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; + int i; + uint16_t addr[3]; + + bcopy(macaddr, addr, sizeof (addr)); + + mutex_enter(&pcnp->pcn_intrlock); + mutex_enter(&pcnp->pcn_xmtlock); + + if (IS_RUNNING(pcnp)) + pcn_suspend(pcnp); + + for (i = 0; i < 3; i++) + pcn_csr_write(pcnp, PCN_CSR_PAR0 + i, addr[i]); + + bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL); + + if (IS_RUNNING(pcnp)) + pcn_resume(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; + + /* enable tx interrupt */ + PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_LTINTEN); + return (B_FALSE); + } + + txsend = pcnp->pcn_txsend; + + /* + * We copy the packet to a single buffer. NetBSD sources suggest + * that if multiple segements are ever used, VMware has a bug that will + * only allow 8 segments to be used, while the physical chips allow 16 + */ + 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++; + } + + tmd = &pcnp->pcn_txdescp[txsend]; + + SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV); + tmd->pcn_txstat = 0; + tmd->pcn_tbaddr = txb->pb_paddr; + + /* PCNet wants the 2's complement of the length of the buffer */ + tmd->pcn_txctl = (~(len) + 1) & PCN_TXCTL_BUFSZ; + tmd->pcn_txctl |= PCN_TXCTL_MBO; + tmd->pcn_txctl |= PCN_TXCTL_STP | PCN_TXCTL_ENP | PCN_TXCTL_ADD_FCS | + PCN_TXCTL_OWN | PCN_TXCTL_MORE_LTINT; + + 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; + + /* Disable TX interrupt */ + PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_LTINTEN); + + mac_tx_update(pcnp->pcn_mh); + } + } +} + +static unsigned +pcn_intr(caddr_t arg1) +{ + pcn_t *pcnp = (void *)arg1; + mblk_t *mp = NULL; + uint32_t status, status2; + boolean_t do_reset = B_FALSE; + + mutex_enter(&pcnp->pcn_intrlock); + + if (IS_SUSPENDED(pcnp)) { + mutex_exit(&pcnp->pcn_intrlock); + return (DDI_INTR_UNCLAIMED); + } + + while ((status = pcn_csr_read(pcnp, PCN_CSR_CSR)) & PCN_CSR_INTR) { + pcn_csr_write(pcnp, PCN_CSR_CSR, status); + + status2 = pcn_csr_read(pcnp, PCN_CSR_EXTCTL2); + + 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; + } + + /* timer interrupt */ + if (status2 & PCN_EXTCTL2_STINT) { + /* ack it */ + PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2, + PCN_EXTCTL2_STINT); + + if (pcn_watchdog(pcnp) != DDI_SUCCESS) { + 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, boolean_t getfact) +{ + int i; + uint16_t addr[3]; + + bcopy(pcnp->pcn_addr, addr, sizeof (addr)); + + /* + * 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. + */ + (void) CSR_READ_2(pcnp, PCN_IO16_RESET); + (void) CSR_READ_4(pcnp, PCN_IO32_RESET); + + drv_usecwait(1000); + + /* Select 32-bit (DWIO) mode */ + CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0); + + /* The timer is not affected by a reset, so explicitly disable */ + pcn_stop_timer(pcnp); + + /* Enable fast suspend */ + pcn_csr_write(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); + + /* Select Style 3 descriptors */ + pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI); + + /* Set MAC address */ + if (getfact) + pcn_getfactaddr(pcnp); + + pcn_csr_write(pcnp, PCN_CSR_PAR0, addr[0]); + pcn_csr_write(pcnp, PCN_CSR_PAR1, addr[1]); + pcn_csr_write(pcnp, PCN_CSR_PAR2, addr[2]); + + /* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */ + PCN_BCR_CLRBIT(pcnp, PCN_BCR_MISCCFG, PCN_MISC_ASEL); + + /* + * XXX: need to find a way to determine when 10bt media is + * selected for non Am79C978, and set to PCN_PORT_10BASET + * instead of PCN_PORT_MII + */ + pcn_csr_write(pcnp, PCN_CSR_MODE, PCN_PORT_MII); + + /* Reenable auto negotiation for external phy */ + PCN_BCR_SETBIT(pcnp, PCN_BCR_MIICTL, PCN_MIICTL_XPHYANE); + + if (pcnp->pcn_promisc) + PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC); + + /* Initalize mcast addr filter */ + for (i = 0; i < 4; i++) + pcn_csr_write(pcnp, PCN_CSR_MAR0 + i, pcnp->pcn_mctab[i]); + + pcn_resetrings(pcnp); + + /* We're not using the initialization block. */ + pcn_csr_write(pcnp, PCN_CSR_IAB1, 0); + + /* + * Enable burst read and write. Also set the no underflow + * bit. This will avoid transmit underruns in ceratin + * conditions while still providing decent performance. + */ + PCN_BCR_SETBIT(pcnp, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW | + PCN_BUSCTL_BREAD | PCN_BUSCTL_BWRITE); + + /* Enable graceful recovery from underflow. */ + PCN_CSR_SETBIT(pcnp, PCN_CSR_IMR, PCN_IMR_DXSUFLO); + + /* Enable auto-padding of short TX frames. */ + PCN_CSR_SETBIT(pcnp, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); + + if (pcnp->pcn_type == Am79C978) + pcn_bcr_write(pcnp, PCN_BCR_PHYSEL, + PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); + + return (DDI_SUCCESS); +} + +static void +pcn_resetall(pcn_t *pcnp) +{ + pcn_stopall(pcnp); + pcn_startall(pcnp); +} + +static void +pcn_startall(pcn_t *pcnp) +{ + ASSERT(mutex_owned(&pcnp->pcn_intrlock)); + ASSERT(mutex_owned(&pcnp->pcn_xmtlock)); + + (void) pcn_initialize(pcnp, B_FALSE); + + /* Start chip and enable interrupts */ + PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_START|PCN_CSR_INTEN); + + pcn_start_timer(pcnp); + + if (IS_RUNNING(pcnp)) + mac_tx_update(pcnp->pcn_mh); +} + +static void +pcn_stopall(pcn_t *pcnp) +{ + ASSERT(mutex_owned(&pcnp->pcn_intrlock)); + ASSERT(mutex_owned(&pcnp->pcn_xmtlock)); + + pcn_stop_timer(pcnp); + PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP); +} + +/* + * The soft timer is not affected by a soft reset (according to the datasheet) + * so it must always be explicitly enabled and disabled + */ +static void +pcn_start_timer(pcn_t *pcnp) +{ + PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN); + + /* + * The frequency this fires varies based on the particular + * model, this value is largely arbitrary. It just needs to + * fire often enough to detect a stall + */ + pcn_bcr_write(pcnp, PCN_BCR_TIMER, 0xa000); +} + + +static void +pcn_stop_timer(pcn_t *pcnp) +{ + PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN); +} + +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 sz, + void *val) +{ + pcn_t *pcnp = (pcn_t *)arg; + + return (mii_m_getprop(pcnp->pcn_mii, name, num, sz, val)); +} + +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 void +pcn_m_propinfo(void *arg, const char *name, mac_prop_id_t num, + mac_prop_info_handle_t prh) +{ + pcn_t *pcnp = arg; + + mii_m_propinfo(pcnp->pcn_mii, name, num, prh); +} + +static int +pcn_watchdog(pcn_t *pcnp) +{ + if ((pcnp->pcn_txstall_time != 0) && + (gethrtime() > pcnp->pcn_txstall_time) && + (pcnp->pcn_txavail != PCN_TXRING)) { + pcnp->pcn_txstall_time = 0; + pcn_error(pcnp->pcn_dip, "TX stall detected!"); + return (DDI_FAILURE); + } else { + return (DDI_SUCCESS); + } +} + +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 != -1 && + phy != pcnp->pcn_extphyaddr) { + return (0); + } + + val = ((uint16_t)phy << 5) | reg; + pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, phy << 5 | reg); + val = pcn_bcr_read(pcnp, PCN_BCR_MIIDATA) & 0xFFFF; + if (val == 0xFFFF) { + return (0); + } + + if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 || + pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr == -1) + 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 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); + + bcopy(&addr[0], &pcnp->pcn_addr[0], sizeof (pcnp->pcn_addr)); +} + +static uint32_t +pcn_csr_read(pcn_t *pcnp, uint32_t reg) +{ + uint32_t val; + + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg); + val = CSR_READ_4(pcnp, PCN_IO32_RDP); + mutex_exit(&pcnp->pcn_reglock); + return (val); +} + +static uint16_t +pcn_csr_read16(pcn_t *pcnp, uint32_t reg) +{ + uint16_t val; + + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg); + val = CSR_READ_2(pcnp, PCN_IO16_RDP); + mutex_exit(&pcnp->pcn_reglock); + return (val); +} + +static void +pcn_csr_write(pcn_t *pcnp, uint32_t reg, uint32_t val) +{ + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg); + CSR_WRITE_4(pcnp, PCN_IO32_RDP, val); + mutex_exit(&pcnp->pcn_reglock); +} + +static uint32_t +pcn_bcr_read(pcn_t *pcnp, uint32_t reg) +{ + uint32_t val; + + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg); + val = CSR_READ_4(pcnp, PCN_IO32_BDP); + mutex_exit(&pcnp->pcn_reglock); + return (val); +} + +static uint16_t +pcn_bcr_read16(pcn_t *pcnp, uint32_t reg) +{ + uint16_t val; + + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg); + val = CSR_READ_2(pcnp, PCN_IO16_BDP); + mutex_exit(&pcnp->pcn_reglock); + return (val); +} + +static void +pcn_bcr_write(pcn_t *pcnp, uint32_t reg, uint32_t val) +{ + mutex_enter(&pcnp->pcn_reglock); + CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg); + CSR_WRITE_4(pcnp, PCN_IO32_BDP, val); + mutex_exit(&pcnp->pcn_reglock); +} + +static void +pcn_resetrings(pcn_t *pcnp) +{ + int i; + uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO; + + pcnp->pcn_rxhead = 0; + pcnp->pcn_txreclaim = 0; + pcnp->pcn_txsend = 0; + pcnp->pcn_txavail = PCN_TXRING; + + /* 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_rxlen = rmd->pcn_rsvd0 = 0; + rmd->pcn_rbaddr = rxb->pb_paddr; + rmd->pcn_bufsz = bufsz; + rmd->pcn_rxstat = PCN_RXSTAT_OWN; + } + (void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0, + PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV); + + /* reset tx descriptor values */ + for (i = 0; i < PCN_TXRING; i++) { + pcn_tx_desc_t *txd = &pcnp->pcn_txdescp[i]; + pcn_buf_t *txb = pcnp->pcn_txbufs[i]; + + txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0; + txd->pcn_tbaddr = txb->pb_paddr; + } + (void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0, + PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV); + + /* 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); +} + +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; + + if (pcnp->pcn_txbufs) { + for (i = 0; i < PCN_TXRING; i++) + pcn_destroybuf(pcnp->pcn_txbufs[i]); + + 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; + + if (pcnp->pcn_rxbufs) { + for (i = 0; i < PCN_RXRING; i++) + pcn_destroybuf(pcnp->pcn_rxbufs[i]); + + 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 int +pcn_set_chipid(pcn_t *pcnp, uint32_t conf_id) +{ + char *name = NULL; + uint32_t chipid; + + /* + * Note: we can *NOT* put the chip into 32-bit mode yet. If a + * lance ethernet device is present and pcn tries to attach, it can + * hang the device (requiring a hardware reset), since they only work + * in 16-bit mode. + * + * The solution is check using 16-bit operations first, and determine + * if 32-bit mode operations are supported. + * + * 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); + + /* + * 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); + } + + chipid = CHIPID_PARTID(chipid); + + /* Set default value and override as needed */ + switch (chipid) { + case Am79C970: + name = "Am79C970 PCnet-PCI"; + pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name); + return (DDI_FAILURE); + case Am79C970A: + name = "Am79C970A PCnet-PCI II"; + pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name); + return (DDI_FAILURE); + case Am79C971: + name = "Am79C971 PCnet-FAST"; + break; + case Am79C972: + name = "Am79C972 PCnet-FAST+"; + break; + case Am79C973: + name = "Am79C973 PCnet-FAST III"; + break; + case Am79C975: + name = "Am79C975 PCnet-FAST III"; + break; + case Am79C976: + name = "Am79C976"; + break; + case Am79C978: + name = "Am79C978"; + break; + default: + name = "Unknown"; + pcn_error(pcnp->pcn_dip, "Unknown chip id 0x%x", chipid); + } + + if (ddi_prop_update_string(DDI_DEV_T_NONE, pcnp->pcn_dip, "chipid", + name) != DDI_SUCCESS) { + pcn_error(pcnp->pcn_dip, "Unable to set chipid property"); + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + +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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pcn/pcn.h Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,450 @@ +/* + * Copyright 2011 Jason King. + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * + */ + +#ifndef _PCN_H +#define _PCN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 16-bit I/O map + * To switch to 32-bit mode, write to RDP. + */ +#define PCN_IO16_APROM00 0x00 +#define PCN_IO16_APROM01 0x02 +#define PCN_IO16_APROM02 0x04 +#define PCN_IO16_APROM03 0x06 +#define PCN_IO16_APROM04 0x08 +#define PCN_IO16_APROM05 0x0A +#define PCN_IO16_APROM06 0x0C +#define PCN_IO16_APROM07 0x0E +#define PCN_IO16_RDP 0x10 +#define PCN_IO16_RAP 0x12 +#define PCN_IO16_RESET 0x14 +#define PCN_IO16_BDP 0x16 + +/* + * 32-bit I/O map + */ +#define PCN_IO32_APROM00 0x00 +#define PCN_IO32_APROM01 0x04 +#define PCN_IO32_APROM02 0x08 +#define PCN_IO32_APROM03 0x0C +#define PCN_IO32_RDP 0x10 +#define PCN_IO32_RAP 0x14 +#define PCN_IO32_RESET 0x18 +#define PCN_IO32_BDP 0x1C + +/* + * CSR registers + */ +#define PCN_CSR_CSR 0x00 +#define PCN_CSR_IAB0 0x01 +#define PCN_CSR_IAB1 0x02 +#define PCN_CSR_IMR 0x03 +#define PCN_CSR_TFEAT 0x04 +#define PCN_CSR_EXTCTL1 0x05 +#define PCN_CSR_DTBLLEN 0x06 +#define PCN_CSR_EXTCTL2 0x07 +#define PCN_CSR_MAR0 0x08 +#define PCN_CSR_MAR1 0x09 +#define PCN_CSR_MAR2 0x0A +#define PCN_CSR_MAR3 0x0B +#define PCN_CSR_PAR0 0x0C +#define PCN_CSR_PAR1 0x0D +#define PCN_CSR_PAR2 0x0E +#define PCN_CSR_MODE 0x0F +#define PCN_CSR_RXADDR0 0x18 +#define PCN_CSR_RXADDR1 0x19 +#define PCN_CSR_TXADDR0 0x1E +#define PCN_CSR_TXADDR1 0x1F +#define PCN_CSR_TXPOLL 0x2F +#define PCN_CSR_RXPOLL 0x31 +#define PCN_CSR_RXRINGLEN 0x4C +#define PCN_CSR_TXRINGLEN 0x4E +#define PCN_CSR_DMACTL 0x50 +#define PCN_CSR_BUSTIMER 0x52 +#define PCN_CSR_MEMERRTIMEO 0x64 +#define PCN_CSR_ONNOWMISC 0x74 +#define PCN_CSR_ADVFEAT 0x7A +#define PCN_CSR_MACCFG 0x7D +#define PCN_CSR_CHIPID0 0x58 +#define PCN_CSR_CHIPID1 0x59 + + +#define PCN_CSR_INIT 0x0001 +#define PCN_CSR_START 0x0002 +#define PCN_CSR_STOP 0x0004 +#define PCN_CSR_TX 0x0008 +#define PCN_CSR_TXON 0x0010 +#define PCN_CSR_RXON 0x0020 +#define PCN_CSR_INTEN 0x0040 +#define PCN_CSR_INTR 0x0080 +#define PCN_CSR_IDONE 0x0100 +#define PCN_CSR_TINT 0x0200 +#define PCN_CSR_RINT 0x0400 +#define PCN_CSR_MERR 0x0800 +#define PCN_CSR_MISS 0x1000 +#define PCN_CSR_CERR 0x2000 +#define PCN_CSR_ERR 0x8000 +#define PCN_CSR_STR \ + "\020" \ + "\001INIT" \ + "\002START" \ + "\003STOP" \ + "\004TX" \ + "\005TXON" \ + "\006RXON" \ + "\007INTEN" \ + "\010INTR" \ + "\011IDONE" \ + "\012TINT" \ + "\013RINT" \ + "\014MERR" \ + "\015MISS" \ + "\016CERR" \ + "\017ERR" + +/* + * Interrupt masks and deferral control (CSR3) + */ +#define PCN_IMR_BSWAP 0x0004 +#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */ +#define PCN_IMR_DXMT2PD 0x0010 +#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */ +#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */ +#define PCN_IMR_IDONE 0x0100 +#define PCN_IMR_TINT 0x0200 +#define PCN_IMR_RINT 0x0400 +#define PCN_IMR_MERR 0x0800 +#define PCN_IMR_MISS 0x1000 +#define PCN_IMR_STR \ + "\020" \ + "\003BSWAP" \ + "\004ENMBA" \ + "\005DXMT2PD" \ + "\006LAPPEN" \ + "\007DXSUFLO" \ + "\010IDONE" \ + "\011TINT" \ + "\012RINT" \ + "\013MERR" \ + "\014MISS" + +/* + * Test and features control (CSR4) + */ +#define PCN_TFEAT_TXSTRTMASK 0x0004 +#define PCN_TFEAT_TXSTRT 0x0008 +#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */ +#define PCN_TFEAT_RXCCOFLOW 0x0020 +#define PCN_TFEAT_UINT 0x0040 +#define PCN_TFEAT_UINTREQ 0x0080 +#define PCN_TFEAT_MISSOFLOWM 0x0100 +#define PCN_TFEAT_MISSOFLOW 0x0200 +#define PCN_TFEAT_STRIP_FCS 0x0400 +#define PCN_TFEAT_PAD_TX 0x0800 +#define PCN_TFEAT_TXDPOLL 0x1000 +#define PCN_TFEAT_DMAPLUS 0x4000 + +/* + * Extended control and interrupt 1 (CSR5) + */ +#define PCN_EXTCTL1_SPND 0x0001 /* suspend */ +#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */ +#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */ +#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */ +#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */ +#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */ +#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */ +#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */ +#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */ +#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */ +#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */ +#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */ +#define PCN_EXTCTL1_STR \ + "\020" \ + "\001SPND" \ + "\002MPMODE" \ + "\003MPENB" \ + "\004MPINTEN" \ + "\005MPINT" \ + "\006MPPLB" \ + "\007EXDEFEN" \ + "\010EXDEF" \ + "\013SINTEN" \ + "\014SINT" \ + "\017LTINTEN" \ + "\020TXOKINTD" + +/* + * RX/TX descriptor len (CSR6) + */ +#define PCN_DTBLLEN_RLEN 0x0F00 +#define PCN_DTBLLEN_TLEN 0xF000 + +/* + * Extended control and interrupt 2 (CSR7) + */ +#define PCN_EXTCTL2_MIIPDTINTE 0x0001 +#define PCN_EXTCTL2_MIIPDTINT 0x0002 +#define PCN_EXTCTL2_MCCIINTE 0x0004 +#define PCN_EXTCTL2_MCCIINT 0x0008 +#define PCN_EXTCTL2_MCCINTE 0x0010 +#define PCN_EXTCTL2_MCCINT 0x0020 +#define PCN_EXTCTL2_MAPINTE 0x0040 +#define PCN_EXTCTL2_MAPINT 0x0080 +#define PCN_EXTCTL2_MREINTE 0x0100 +#define PCN_EXTCTL2_MREINT 0x0200 +#define PCN_EXTCTL2_STINTE 0x0400 +#define PCN_EXTCTL2_STINT 0x0800 +#define PCN_EXTCTL2_RXDPOLL 0x1000 +#define PCN_EXTCTL2_RDMD 0x2000 +#define PCN_EXTCTL2_RXFRTG 0x4000 +#define PCN_EXTCTL2_FASTSPNDE 0x8000 +#define PCN_EXTCTL2_STR \ + "\020" \ + "\001MIIPDTINTE" \ + "\002MIIPDTINT" \ + "\003MCCIINTTE" \ + "\004MCCIINT" \ + "\005MCCINTE" \ + "\006MCCINT" \ + "\007MAPINTE" \ + "\010MAPINT" \ + "\011MRTINTE" \ + "\012MREINT" \ + "\013STINTE" \ + "\014STINT" \ + "\015RXDPOLL" \ + "\016RDMD" \ + "\017RXFRTG" \ + "\020FASTSPNDE" + +/* + * Mode (CSR15) + */ +#define PCN_MODE_RXD 0x0001 /* RX disable */ +#define PCN_MODE_TXD 0x0002 /* TX disable */ +#define PCN_MODE_LOOP 0x0004 /* loopback enable */ +#define PCN_MODE_TXCRCD 0x0008 +#define PCN_MODE_FORCECOLL 0x0010 +#define PCN_MODE_RETRYD 0x0020 +#define PCN_MODE_INTLOOP 0x0040 +#define PCN_MODE_PORTSEL 0x0180 +#define PCN_MODE_RXVPAD 0x2000 +#define PCN_MODE_RXNOBROAD 0x4000 +#define PCN_MODE_PROMISC 0x8000 +#define PCN_MODE_STR \ + "\020" \ + "\001RXD" \ + "\002TXD" \ + "\003LOOP" \ + "\004TXCRCD" \ + "\005FORCECOLL" \ + "\006RETRYD" \ + "\007INTLOOP" \ + "\016RXVPAD" \ + "\017RXNOBROAD" \ + "\020PROMISC" + +/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */ +#define PCN_PORT_AUI 0x0000 +#define PCN_PORT_10BASET 0x0080 +#define PCN_PORT_GPSI 0x0100 +#define PCN_PORT_MII 0x0180 + +/* + * Chip ID values. + */ + +#define CHIPID_MANFID(x) (((x) >> 1) & 0x3ff) +#define CHIPID_PARTID(x) (((x) >> 12) & 0xffff) +#define CHIPID_VER(x) (((x) >> 28) & 0x7) + +/* CSR88-89: Chip ID masks */ +#define Am79C970 0x0003 +#define Am79C970A 0x2621 +#define Am79C971 0x2623 +#define Am79C972 0x2624 +#define Am79C973 0x2625 +#define Am79C978 0x2626 +#define Am79C975 0x2627 +#define Am79C976 0x2628 + +/* + * Advanced feature control (CSR122) + */ +#define PCN_AFC_RXALIGN 0x0001 + +/* + * BCR (bus control) registers + */ +#define PCN_BCR_MMRA 0x00 /* Master Mode Read Active */ +#define PCN_BCR_MMW 0x01 /* Master Mode Write Active */ +#define PCN_BCR_MISCCFG 0x02 +#define PCN_BCR_LED0 0x04 +#define PCN_BCR_LED1 0x05 +#define PCN_BCR_LED2 0x06 +#define PCN_BCR_LED3 0x07 +#define PCN_BCR_DUPLEX 0x09 +#define PCN_BCR_BUSCTL 0x12 +#define PCN_BCR_EECTL 0x13 +#define PCN_BCR_SSTYLE 0x14 +#define PCN_BCR_PCILAT 0x16 +#define PCN_BCR_PCISUBVENID 0x17 +#define PCN_BCR_PCISUBSYSID 0x18 +#define PCN_BCR_SRAMSIZE 0x19 +#define PCN_BCR_SRAMBOUND 0x1A +#define PCN_BCR_SRAMCTL 0x1B +#define PCN_BCR_TIMER 0x1F +#define PCN_BCR_MIICTL 0x20 +#define PCN_BCR_MIIADDR 0x21 +#define PCN_BCR_MIIDATA 0x22 +#define PCN_BCR_PCIVENID 0x23 +#define PCN_BCR_PCIPCAP 0x24 +#define PCN_BCR_DATA0 0x25 +#define PCN_BCR_DATA1 0x26 +#define PCN_BCR_DATA2 0x27 +#define PCN_BCR_DATA3 0x28 +#define PCN_BCR_DATA4 0x29 +#define PCN_BCR_DATA5 0x2A +#define PCN_BCR_DATA6 0x2B +#define PCN_BCR_DATA7 0x2C +#define PCN_BCR_ONNOWPAT0 0x2D +#define PCN_BCR_ONNOWPAT1 0x2E +#define PCN_BCR_ONNOWPAT2 0x2F +#define PCN_BCR_PHYSEL 0x31 + +/* + * Miscellaneous Configuration (BCR2) + */ +#define PCN_MISC_TMAULOOP 1<<14 /* T-MAU Loopback packet enable. */ +#define PCN_MISC_LEDPE 1<<12 /* LED Program Enable */ +#define PCN_MISC_APROMWE 1<<8 /* Address PROM Write Enable */ +#define PCN_MISC_INTLEVEL 1<<7 /* Interrupt level */ +#define PCN_MISC_EADISEL 1<<3 /* EADI Select */ +#define PCN_MISC_AWAKE 1<<2 /* Power saving mode select */ +#define PCN_MISC_ASEL 1<<1 /* Auto Select */ +#define PCN_MISC_XMAUSEL 1<<0 /* Reserved. */ + +/* + * Full duplex control (BCR9) + */ +#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ +#define PCN_DUPLEX_AUI 0x0002 /* AUI full-duplex */ +#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */ + +/* + * Burst and bus control register (BCR18) + */ +#define PCN_BUSCTL_BWRITE 0x0020 +#define PCN_BUSCTL_BREAD 0x0040 +#define PCN_BUSCTL_DWIO 0x0080 +#define PCN_BUSCTL_EXTREQ 0x0100 +#define PCN_BUSCTL_MEMCMD 0x0200 +#define PCN_BUSCTL_NOUFLOW 0x0800 +#define PCN_BUSCTL_ROMTMG 0xF000 + +/* + * EEPROM control (BCR19) + */ +#define PCN_EECTL_EDATA 0x0001 +#define PCN_EECTL_ECLK 0x0002 +#define PCN_EECTL_EECS 0x0004 +#define PCN_EECTL_EEN 0x0100 +#define PCN_EECTL_EEDET 0x2000 +#define PCN_EECTL_PREAD 0x4000 +#define PCN_EECTL_PVALID 0x8000 + +/* + * Software style (BCR20) + */ +#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */ +#define PCN_SSTYLE_SSIZE32 0x0100 +#define PCN_SSTYLE_SWSTYLE 0x00FF + +#define PCN_SWSTYLE_LANCE 0x0000 +#define PCN_SWSTYLE_PCNETPCI 0x0102 +#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103 + +/* + * MII control and status (BCR32) + */ +#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */ +#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */ +#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */ +#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */ +#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */ +#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */ +#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */ +#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */ +#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */ +#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */ +#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */ + +/* + * MII address register (BCR33) + */ +#define PCN_MIIADDR_REGAD 0x001F +#define PCN_MIIADDR_PHYAD 0x03E0 + +/* addresses of internal PHYs */ +#define PCN_PHYAD_100BTX 30 +#define PCN_PHYAD_10BT 31 + +/* + * MII data register (BCR34) + */ +#define PCN_MIIDATA_MIIMD 0xFFFF + +/* + * PHY selection (BCR49) (HomePNA NIC only) + */ +#define PCN_PHYSEL_PHYSEL 0x0003 +#define PCN_PHYSEL_DEFAULT 0x0300 +#define PCN_PHYSEL_PCNET 0x8000 + +#define PCN_PHY_10BT 0x0000 +#define PCN_PHY_HOMEPNA 0x0001 +#define PCN_PHY_EXTERNAL 0x0002 + +#ifdef __cplusplus +} +#endif + +#endif /* _PCN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pcn/pcnimpl.h Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2011 Jason King. + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. 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. + * + */ +#ifndef _PCNIMPL_H +#define _PCNIMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL + +#define PCN_VENDORID 0x1022 +#define PCN_DEVICEID_PCNET 0x2000 +#define PCN_DEVICEID_HOME 0x2001 + +typedef struct pcn_type { + uint16_t pcn_vid; + uint16_t pcn_did; + char *pcn_name; /* ddi_set_prop takes char * */ +} pcn_type_t; + +#define PCN_TXRECLAIM 8 +#define PCN_HEADROOM 34 +#define PCN_TXRESCHED 120 + +#define PCN_RXSTAT_BAM 0x0008 /* broadcast address match */ +#define PCN_RXSTAT_LAFM 0x0010 /* logical address filter match */ +#define PCN_RXSTAT_PAM 0x0020 /* physical address match */ +#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */ +#define PCN_RXSTAT_ENP 0x0100 /* end of packet */ +#define PCN_RXSTAT_STP 0x0200 /* start of packet */ +#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */ +#define PCN_RXSTAT_CRC 0x0800 /* CRC error */ +#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */ +#define PCN_RXSTAT_FRAM 0x2000 /* framing error */ +#define PCN_RXSTAT_ERR 0x4000 /* error summary */ +#define PCN_RXSTAT_OWN 0x8000 +#define PCN_RXSTAT_STR \ + "\020" \ + "\004BAM" \ + "\005LAFM" \ + "\006PAM" \ + "\010BPE" \ + "\011ENP" \ + "\012STP" \ + "\013BUFF" \ + "\014CRC" \ + "\015OFLOW" \ + "\016FRAM" \ + "\017ERR" \ + "\020OWN" + +#define PCN_RXLEN_MBO 0xF000 +#define PCN_RXLEN_BUFSZ 0x0FFF + +typedef struct pcn_rx_desc { + uint16_t pcn_rxlen; + uint16_t pcn_rsvd0; + uint16_t pcn_bufsz; + uint16_t pcn_rxstat; + uint32_t pcn_rbaddr; + uint32_t pcn_uspace; +} pcn_rx_desc_t; + +typedef struct pcn_tx_desc { + uint32_t pcn_txstat; + uint32_t pcn_txctl; + uint32_t pcn_tbaddr; + uint32_t pcn_uspace; +} pcn_tx_desc_t; + +#define PCN_TXCTL_OWN 0x80000000 +#define PCN_TXCTL_ERR 0x40000000 /* error summary */ +#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */ +#define PCN_TXCTL_MORE_LTINT 0x10000000 +#define PCN_TXCTL_ONE 0x08000000 +#define PCN_TXCTL_DEF 0x04000000 +#define PCN_TXCTL_STP 0x02000000 +#define PCN_TXCTL_ENP 0x01000000 +#define PCN_TXCTL_BPE 0x00800000 +#define PCN_TXCTL_MBO 0x0000F000 +#define PCN_TXCTL_BUFSZ 0x00000FFF +#define PCN_TXCTL_STR \ + "\020" \ + "\040OWN" \ + "\037ERR" \ + "\036ADD_FCS" \ + "\035MORE_LTINT" \ + "\034ONE" \ + "\033DEF" \ + "\032STP" \ + "\031ENP" \ + "\030BPE" + +typedef struct pcn_buf { + caddr_t pb_buf; + ddi_dma_handle_t pb_dmah; + ddi_acc_handle_t pb_acch; + uint32_t pb_paddr; +} pcn_buf_t; + +/* Constants, do not change */ +#define PCN_BUFSZ (1664) +#define PCN_MCHASH (64) + +/* Number of descriptor entries */ +#define PCN_RXRING 64 +#define PCN_TXRING 256 + +typedef struct pcn { + dev_info_t *pcn_dip; + mac_handle_t pcn_mh; + mii_handle_t pcn_mii; + uint16_t pcn_cachesize; + int pcn_flags; + int pcn_instance; + kmutex_t pcn_xmtlock; + kmutex_t pcn_intrlock; + kmutex_t pcn_reglock; + ddi_iblock_cookie_t pcn_icookie; + uint_t pcn_int_pri; + int pcn_type; + int8_t pcn_extphyaddr; + + /* + * Register and DMA access + */ + uintptr_t pcn_regs; + ddi_acc_handle_t pcn_regshandle; + + /* + * Receive descriptors. + */ + int pcn_rxhead; + pcn_rx_desc_t *pcn_rxdescp; + ddi_dma_handle_t pcn_rxdesc_dmah; + ddi_acc_handle_t pcn_rxdesc_acch; + uint32_t pcn_rxdesc_paddr; + pcn_buf_t **pcn_rxbufs; + + /* + * Transmit descriptors. + */ + int pcn_txreclaim; + int pcn_txsend; + int pcn_txavail; + pcn_tx_desc_t *pcn_txdescp; + ddi_dma_handle_t pcn_txdesc_dmah; + ddi_acc_handle_t pcn_txdesc_acch; + uint32_t pcn_txdesc_paddr; + pcn_buf_t **pcn_txbufs; + hrtime_t pcn_txstall_time; + boolean_t pcn_wantw; + + /* + * Address management. + */ + uchar_t pcn_addr[ETHERADDRL]; + boolean_t pcn_promisc; + uint16_t pcn_mccount[PCN_MCHASH]; + uint16_t pcn_mctab[PCN_MCHASH / 16]; + + /* + * stats + */ + uint64_t pcn_ipackets; + uint64_t pcn_opackets; + uint64_t pcn_rbytes; + uint64_t pcn_obytes; + uint64_t pcn_brdcstxmt; + uint64_t pcn_multixmt; + uint64_t pcn_brdcstrcv; + uint64_t pcn_multircv; + uint64_t pcn_norcvbuf; + uint64_t pcn_errrcv; + uint64_t pcn_errxmt; + uint64_t pcn_missed; + uint64_t pcn_underflow; + uint64_t pcn_overflow; + uint64_t pcn_align_errors; + uint64_t pcn_fcs_errors; + uint64_t pcn_carrier_errors; + uint64_t pcn_collisions; + uint64_t pcn_ex_collisions; + uint64_t pcn_tx_late_collisions; + uint64_t pcn_defer_xmts; + uint64_t pcn_first_collisions; + uint64_t pcn_multi_collisions; + uint64_t pcn_sqe_errors; + uint64_t pcn_macxmt_errors; + uint64_t pcn_macrcv_errors; + uint64_t pcn_toolong_errors; + uint64_t pcn_runt; + uint64_t pcn_jabber; +} pcn_t; + +/* Flags */ +#define PCN_RUNNING (1L << 0) +#define PCN_SUSPENDED (1L << 1) +#define PCN_INTR_ENABLED (1L << 2) +#define PCN_FLAGSTR \ + "\020" \ + "\001RUNNING" \ + "\002SUSPENDED" \ + "\003INTR_ENABLED" +#define IS_RUNNING(p) ((p)->pcn_flags & PCN_RUNNING) +#define IS_SUSPENDED(p) ((p)->pcn_flags & PCN_SUSPENDED) + +#define SYNCTXDESC(pcnp, index, who) \ + (void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, \ + (index * sizeof (pcn_tx_desc_t)), sizeof (pcn_tx_desc_t), who) + +#define SYNCRXDESC(pcnp, index, who) \ + (void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, \ + (index * sizeof (pcn_rx_desc_t)), sizeof (pcn_rx_desc_t), who) + +#define SYNCBUF(pb, len, who) \ + (void) ddi_dma_sync(pb->pb_dmah, 0, len, who) + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _PCNIMPL_H */
--- a/usr/src/uts/intel/Makefile.intel.shared Thu Sep 22 13:37:45 2011 +0400 +++ b/usr/src/uts/intel/Makefile.intel.shared Mon Sep 26 20:45:52 2011 -0400 @@ -377,7 +377,6 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += bcm_sata $(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest $(CLOSED_BUILD)CLOSED_DRV_KMODS += mpt -$(CLOSED_BUILD)CLOSED_DRV_KMODS += pcn $(CLOSED_BUILD)CLOSED_DRV_KMODS += atiatom $(CLOSED_BUILD)CLOSED_DRV_KMODS += acpi_toshiba @@ -395,6 +394,7 @@ DRV_KMODS += hme DRV_KMODS += mxfe DRV_KMODS += nge +DRV_KMODS += pcn DRV_KMODS += rge DRV_KMODS += rtls DRV_KMODS += sfe
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/pcn/Makefile Mon Sep 26 20:45:52 2011 -0400 @@ -0,0 +1,88 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2011 Jason King. All rights reserved. +# Use is subject to license terms. +# +# This makefile drives the production of the AMD Pcnet +# Ethernet (pcn) driver module in intel systems +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcn +OBJECTS = $(PCN_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCN_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides +# + +# +# Driver depends on GLD +# +LDFLAGS += -dy -N misc/mac -Nmisc/mii + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ