dnet.c revision 4ab75253616c6d68e967c10221bb663c0bfa99df
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* dnet -- DEC 21x4x
*
* Currently supports:
* 21040, 21041, 21140, 21142, 21143
* SROM versions 1, 3, 3.03, 4
* TP, AUI, BNC, 100BASETX, 100BASET4
*
* XXX NEEDSWORK
* All media SHOULD work, FX is untested
*
* Depends on the Generic LAN Driver utility functions in /kernel/misc/gld
*/
#define BUG_4010796 /* See 4007871, 4010796 */
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/devops.h>
#include <sys/ksynch.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/debug.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/gld.h>
#include <sys/pci.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/mii.h>
#include <sys/dnet.h>
#ifdef MII_IS_MODULE
#define MII_DEPEND " misc/mii"
#else
#define MII_DEPEND
#endif
char _depends_on[] = "misc/gld" MII_DEPEND;
/*
* Declarations and Module Linkage
*/
#define IDENT "DNET 21x4x"
/*
* #define DNET_NOISY
* #define SROMDEBUG
* #define SROMDUMPSTRUCTURES
*/
#ifdef DNETDEBUG
#ifdef DNET_NOISY
int dnetdebug = -1;
#else
int dnetdebug = 0;
#endif
#endif
/* used for message allocated using desballoc() */
struct free_ptr {
struct free_rtn free_rtn;
caddr_t buf;
};
struct rbuf_list {
struct rbuf_list *rbuf_next; /* next in the list */
caddr_t rbuf_vaddr; /* virual addr of the buf */
caddr_t rbuf_paddr; /* physical addr of the buf */
caddr_t rbuf_endpaddr; /* physical addr at the end */
ddi_dma_handle_t rbuf_dmahdl; /* dma handle */
ddi_acc_handle_t rbuf_acchdl; /* handle for DDI functions */
};
/* Required system entry points */
static int dnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int dnetprobe(dev_info_t *);
static int dnetattach(dev_info_t *, ddi_attach_cmd_t);
static int dnetdetach(dev_info_t *, ddi_detach_cmd_t);
/* Required driver entry points for GLD */
static int dnet_reset(gld_mac_info_t *);
static int dnet_start_board(gld_mac_info_t *);
static int dnet_stop_board(gld_mac_info_t *);
static int dnet_set_mac_addr(gld_mac_info_t *, uchar_t *);
static int dnet_set_multicast(gld_mac_info_t *, uchar_t *, int);
static int dnet_set_promiscuous(gld_mac_info_t *, int);
static int dnet_get_stats(gld_mac_info_t *, struct gld_stats *);
static int dnet_send(gld_mac_info_t *, mblk_t *);
static uint_t dnetintr(gld_mac_info_t *);
/* Internal functions used by the above entry points */
static void write_gpr(struct dnetinstance *dnetp, uint32_t val);
static void dnet_reset_board(gld_mac_info_t *);
static void dnet_init_board(gld_mac_info_t *);
static void dnet_chip_init(gld_mac_info_t *);
static unsigned int hashindex(uchar_t *);
static int dnet_start(gld_mac_info_t *);
static int dnet_set_addr(gld_mac_info_t *, uchar_t *);
static void dnet_getp(gld_mac_info_t *);
static void update_rx_stats(gld_mac_info_t *, int);
static void update_tx_stats(gld_mac_info_t *, int);
/* Media Selection Setup Routines */
static void set_gpr(gld_mac_info_t *macinfo);
static void set_opr(gld_mac_info_t *macinfo);
static void set_sia(gld_mac_info_t *macinfo);
/* Buffer Management Routines */
static int dnet_alloc_bufs(gld_mac_info_t *);
static void dnet_free_bufs(gld_mac_info_t *);
static void dnet_init_txrx_bufs(gld_mac_info_t *);
static int alloc_descriptor(gld_mac_info_t *);
static void dnet_reclaim_Tx_desc(gld_mac_info_t *);
static int dnet_rbuf_init(dev_info_t *, int);
static int dnet_rbuf_destroy();
static struct rbuf_list *dnet_rbuf_alloc(dev_info_t *, int);
static void dnet_rbuf_free(caddr_t);
static void dnet_freemsg_buf(struct free_ptr *);
static void setup_block(gld_mac_info_t *macinfo);
/* SROM read functions */
static int dnet_read_srom(dev_info_t *, int, ddi_acc_handle_t, int, uchar_t *,
int);
static void dnet_read21040addr(dev_info_t *, ddi_acc_handle_t, int, uchar_t *,
int *);
static void dnet_read21140srom(ddi_acc_handle_t, int, uchar_t *, int);
static int get_alternative_srom_image(dev_info_t *, uchar_t *, int);
static void dnet_print_srom(SROM_FORMAT *sr);
static void dnet_dump_leaf(LEAF_FORMAT *leaf);
static void dnet_dump_block(media_block_t *block);
#ifdef BUG_4010796
static void set_alternative_srom_image(dev_info_t *, uchar_t *, int);
static int dnethack(dev_info_t *);
#endif
static int dnet_hack_interrupts(gld_mac_info_t *, int);
static int dnet_detach_hacked_interrupt(dev_info_t *devinfo);
static void enable_interrupts(struct dnetinstance *dnetp, int enable_xmit);
/* SROM parsing functions */
static void dnet_parse_srom(struct dnetinstance *dnetp, SROM_FORMAT *sr,
uchar_t *vi);
static void parse_controller_leaf(struct dnetinstance *dnetp, LEAF_FORMAT *leaf,
uchar_t *vi);
static uchar_t *parse_media_block(struct dnetinstance *dnetp,
media_block_t *block, uchar_t *vi);
static int check_srom_valid(uchar_t *);
static void dnet_dumpbin(char *msg, uchar_t *, int size, int len);
static void setup_legacy_blocks();
/* Active Media Determination Routines */
static void find_active_media(gld_mac_info_t *);
static int send_test_packet(gld_mac_info_t *);
static int dnet_link_sense(gld_mac_info_t *macinfo);
/* PHY MII Routines */
static ushort_t dnet_mii_read(dev_info_t *dip, int phy_addr, int reg_num);
static void dnet_mii_write(dev_info_t *dip, int phy_addr, int reg_num,
int reg_dat);
static void write_mii(struct dnetinstance *, uint32_t, int);
static void mii_tristate(struct dnetinstance *);
static void do_phy(gld_mac_info_t *);
static void dnet_mii_link_cb(dev_info_t *, int, enum mii_phy_state);
static void set_leaf(SROM_FORMAT *sr, LEAF_FORMAT *leaf);
#ifdef DNETDEBUG
uint32_t dnet_usecelapsed(struct dnetinstance *dnetp);
void dnet_timestamp(struct dnetinstance *, char *);
void dnet_usectimeout(struct dnetinstance *, uint32_t, int, timercb_t);
#endif
static char *media_str[] = {
"10BaseT",
"10Base2",
"10Base5",
"100BaseTX",
"10BaseT FD",
"100BaseTX FD",
"100BaseT4",
"100BaseFX",
"100BaseFX FD",
"MII"
};
/* default SROM info for cards with no SROMs */
static LEAF_FORMAT leaf_default_100;
static LEAF_FORMAT leaf_asante;
static LEAF_FORMAT leaf_phylegacy;
static LEAF_FORMAT leaf_cogent_100;
static LEAF_FORMAT leaf_21041;
static LEAF_FORMAT leaf_21040;
int rx_buf_size = (ETHERMAX + ETHERFCSL + 3) & ~3; /* roundup to 4 */
int max_rx_desc_21040 = MAX_RX_DESC_21040;
int max_rx_desc_21140 = MAX_RX_DESC_21140;
int max_tx_desc = MAX_TX_DESC;
int dnet_xmit_threshold = MAX_TX_DESC >> 2; /* XXX need tuning? */
static kmutex_t dnet_rbuf_lock; /* mutex to protect rbuf_list data */
/* used for buffers allocated by ddi_dma_mem_alloc() */
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0, /* dma_attr_addr_lo */
(uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
0x7FFFFFFF, /* dma_attr_count_max */
4, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/* used for buffers allocated for rbuf, allow 2 cookies */
static ddi_dma_attr_t dma_attr_rb = {
DMA_ATTR_V0, /* dma_attr version */
0, /* dma_attr_addr_lo */
(uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
0x7FFFFFFF, /* dma_attr_count_max */
4, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_seg */
2, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/* used for buffers which are NOT from ddi_dma_mem_alloc() - xmit side */
static ddi_dma_attr_t dma_attr_tx = {
DMA_ATTR_V0, /* dma_attr version */
0, /* dma_attr_addr_lo */
(uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
0x7FFFFFFF, /* dma_attr_count_max */
1, /* dma_attr_align */
0x3F, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
(uint64_t)0xFFFFFFFF, /* dma_attr_seg */
0x7FFF, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0, /* dma_attr_flags */
};
static ddi_device_acc_attr_t accattr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC,
};
uchar_t dnet_broadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* Standard Streams initialization */
static struct module_info minfo = {
DNETIDNUM, "dnet", 0, INFPSZ, DNETHIWAT, DNETLOWAT
};
static struct qinit rinit = { /* read queues */
NULL, gld_rsrv, gld_open, gld_close, NULL, &minfo, NULL
};
static struct qinit winit = { /* write queues */
gld_wput, gld_wsrv, NULL, NULL, NULL, &minfo, NULL
};
static struct streamtab dnetinfo = {&rinit, &winit, NULL, NULL};
/* Standard Module linkage initialization for a Streams driver */
extern struct mod_ops mod_driverops;
static struct cb_ops cb_dnetops = {
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&dnetinfo, /* cb_stream */
(int)(D_MP | D_HOTPLUG) /* cb_flag */
};
static struct dev_ops dnetops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
gld_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
dnetprobe, /* devo_probe */
dnetattach, /* devo_attach */
dnetdetach, /* devo_detach */
nodev, /* devo_reset */
&cb_dnetops, /* devo_cb_ops */
(struct bus_ops *)NULL /* devo_bus_ops */
};
static struct modldrv modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
IDENT " %I%", /* short description */
&dnetops /* driver specific ops */
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
/*
* Passed to the hacked interrupt for multiport Cogent and ZNYX cards with
* dodgy interrupt routing
*/
#define MAX_INST 8 /* Maximum instances on a multiport adapter. */
struct hackintr_inf
{
gld_mac_info_t *macinfos[MAX_INST]; /* macinfos for each port */
dev_info_t *devinfo; /* Devinfo of the primary device */
kmutex_t lock;
/* Ensures the interrupt doesn't get called while detaching */
};
static char hackintr_propname[] = "InterruptData";
static char macoffset_propname[] = "MAC_offset";
static char speed_propname[] = "speed";
static char ofloprob_propname[] = "dmaworkaround";
static char duplex_propname[] = "full-duplex"; /* Must agree with MII */
static char printsrom_propname[] = "print-srom";
static uint_t dnet_hack_intr(struct hackintr_inf *);
/*
* ========== Module Loading Entry Points ==========
*/
int
_init(void)
{
int i;
/* Configure fake sroms for legacy cards */
mutex_init(&dnet_rbuf_lock, NULL, MUTEX_DRIVER, NULL);
setup_legacy_blocks();
if ((i = mod_install(&modlinkage)) != 0) {
mutex_destroy(&dnet_rbuf_lock);
}
return (i);
}
int
_fini(void)
{
int i;
if ((i = mod_remove(&modlinkage)) == 0) { /* module can be unloaded */
/* loop until all the receive buffers are freed */
while (dnet_rbuf_destroy() != 0) {
delay(drv_usectohz(100000));
#ifdef DNETDEBUG
if (dnetdebug & DNETDDI)
cmn_err(CE_WARN, "dnet _fini delay");
#endif
}
mutex_destroy(&dnet_rbuf_lock);
}
return (i);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/*
* ========== DDI Entry Points ==========
*/
/*
* probe(9E) -- Determine if a device is present
*/
static int
dnetprobe(dev_info_t *devinfo)
{
ddi_acc_handle_t handle;
uint16_t vendorid;
uint16_t deviceid;
#ifdef DNETDEBUG
if (dnetdebug & DNETDDI)
cmn_err(CE_NOTE, "dnetprobe(0x%p)", (void *) devinfo);
#endif
if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
return (DDI_PROBE_FAILURE);
vendorid = pci_config_get16(handle, PCI_CONF_VENID);
if (vendorid != DEC_VENDOR_ID) {
pci_config_teardown(&handle);
return (DDI_PROBE_FAILURE);
}
deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
switch (deviceid) {
case DEVICE_ID_21040:
case DEVICE_ID_21041:
case DEVICE_ID_21140:
case DEVICE_ID_21143: /* And 142 */
break;
default:
pci_config_teardown(&handle);
return (DDI_PROBE_FAILURE);
}
pci_config_teardown(&handle);
#ifndef BUG_4010796
return (DDI_PROBE_SUCCESS);
#else
return (dnethack(devinfo));
#endif
}
#ifdef BUG_4010796
/*
* If we have a device, but we cannot presently access its SROM data,
* then we return DDI_PROBE_PARTIAL and hope that sometime later we
* will be able to get at the SROM data. This can only happen if we
* are a secondary port with no SROM, and the bootstrap failed to set
* our DNET_SROM property, and our primary sibling has not yet probed.
*/
static int
dnethack(dev_info_t *devinfo)
{
uchar_t vendor_info[SROM_SIZE];
uint32_t csr;
uint16_t deviceid;
ddi_acc_handle_t handle;
uint32_t retval;
int secondary;
ddi_acc_handle_t io_handle;
uint32_t io_reg;
#define DNET_PCI_RNUMBER 1
#ifdef DNETDEBUG
if (dnetdebug & DNETDDI)
cmn_err(CE_NOTE, "dnethack(0x%p)", (void *) devinfo);
#endif
if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
return (DDI_PROBE_FAILURE);
deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
/*
* Turn on Master Enable and IO Enable bits.
*/
csr = pci_config_get32(handle, PCI_CONF_COMM);
pci_config_put32(handle, PCI_CONF_COMM, (csr |PCI_COMM_ME|PCI_COMM_IO));
pci_config_teardown(&handle);
/* Now map I/O register */
if (ddi_regs_map_setup(devinfo, DNET_PCI_RNUMBER,
(caddr_t *)&io_reg, (offset_t)0, (offset_t)0, &accattr,
&io_handle) != DDI_SUCCESS) {
return (DDI_PROBE_FAILURE);
}
/*
* Reset the chip
*/
ddi_put32(io_handle, REG32(io_reg, BUS_MODE_REG), SW_RESET);
drv_usecwait(3);
ddi_put32(io_handle, REG32(io_reg, BUS_MODE_REG), 0);
drv_usecwait(8);
secondary = dnet_read_srom(devinfo, deviceid, io_handle,
io_reg, vendor_info, sizeof (vendor_info));
switch (secondary) {
case -1:
/* We can't access our SROM data! */
retval = DDI_PROBE_PARTIAL;
break;
case 0:
retval = DDI_PROBE_SUCCESS;
break;
default:
retval = DDI_PROBE_SUCCESS;
}
ddi_regs_map_free(&io_handle);
return (retval);
}
#endif /* BUG_4010796 */
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
dnetattach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
uint16_t revid;
struct dnetinstance *dnetp; /* Our private device info */
gld_mac_info_t *macinfo; /* GLD structure */
uchar_t vendor_info[SROM_SIZE];
uint32_t csr;
uint16_t deviceid;
ddi_acc_handle_t handle;
int secondary;
#define DNET_PCI_RNUMBER 1
#ifdef DNETDEBUG
if (dnetdebug & DNETDDI)
cmn_err(CE_NOTE, "dnetattach(0x%p)", (void *) devinfo);
#endif
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (pci_config_setup(devinfo, &handle) != DDI_SUCCESS)
return (DDI_FAILURE);
deviceid = pci_config_get16(handle, PCI_CONF_DEVID);
switch (deviceid) {
case DEVICE_ID_21040:
case DEVICE_ID_21041:
case DEVICE_ID_21140:
case DEVICE_ID_21143: /* And 142 */
break;
default:
pci_config_teardown(&handle);
return (DDI_FAILURE);
}
/*
* Turn on Master Enable and IO Enable bits.
*/
csr = pci_config_get32(handle, PCI_CONF_COMM);
pci_config_put32(handle, PCI_CONF_COMM, (csr |PCI_COMM_ME|PCI_COMM_IO));
/* Make sure the device is not asleep */
csr = pci_config_get32(handle, PCI_DNET_CONF_CFDD);
pci_config_put32(handle, PCI_DNET_CONF_CFDD,
csr & ~(CFDD_SLEEP|CFDD_SNOOZE));
revid = pci_config_get8(handle, PCI_CONF_REVID);
pci_config_teardown(&handle);
/*
* Allocate gld_mac_info_t and dnetinstance structures
*/
if ((macinfo = gld_mac_alloc(devinfo)) == NULL)
return (DDI_FAILURE);
if ((dnetp = (struct dnetinstance *)
kmem_zalloc(sizeof (struct dnetinstance), KM_SLEEP)) == NULL) {
gld_mac_free(macinfo);
return (DDI_FAILURE);
}
/* Now map I/O register */
if (ddi_regs_map_setup(devinfo, DNET_PCI_RNUMBER,
(caddr_t *)&dnetp->io_reg, (offset_t)0, (offset_t)0, &accattr,
&dnetp->io_handle) != DDI_SUCCESS) {
kmem_free(dnetp, sizeof (struct dnetinstance));
gld_mac_free(macinfo);
return (DDI_FAILURE);
}
dnetp->devinfo = devinfo;
dnetp->board_type = deviceid;
/*
* Initialize our private fields in macinfo and dnetinstance
*/
macinfo->gldm_private = (caddr_t)dnetp;
/*
* Initialize pointers to device specific functions which will be
* used by the generic layer.
*/
macinfo->gldm_reset = dnet_reset;
macinfo->gldm_start = dnet_start_board;
macinfo->gldm_stop = dnet_stop_board;
macinfo->gldm_set_mac_addr = dnet_set_mac_addr;
macinfo->gldm_set_multicast = dnet_set_multicast;
macinfo->gldm_set_promiscuous = dnet_set_promiscuous;
macinfo->gldm_get_stats = dnet_get_stats;
macinfo->gldm_send = dnet_send;
macinfo->gldm_intr = dnetintr;
macinfo->gldm_ioctl = NULL;
/*
* Initialize board characteristics needed by the generic layer.
*/
macinfo->gldm_ident = IDENT;
macinfo->gldm_type = DL_ETHER;
macinfo->gldm_minpkt = 0; /* assumes we pad ourselves */
macinfo->gldm_maxpkt = DNETMAXPKT;
macinfo->gldm_addrlen = ETHERADDRL;
macinfo->gldm_saplen = -2;
/* Other required initialization */
macinfo->gldm_ppa = ddi_get_instance(devinfo);
macinfo->gldm_vendor_addr = dnetp->vendor_addr;
macinfo->gldm_broadcast_addr = dnet_broadcastaddr;
macinfo->gldm_devinfo = devinfo;
/*
* Get the iblock cookie with which to initialize the mutexes.
*/
if (ddi_get_iblock_cookie(devinfo, 0, &macinfo->gldm_cookie)
!= DDI_SUCCESS)
goto fail;
/*
* Initialize mutex's for this device.
* Do this before registering the interrupt handler to avoid
* condition where interrupt handler can try using uninitialized
* mutex.
* Lock ordering rules: always lock intrlock first before
* txlock if both are required.
*/
mutex_init(&dnetp->txlock,
NULL, MUTEX_DRIVER, macinfo->gldm_cookie);
mutex_init(&dnetp->intrlock,
NULL, MUTEX_DRIVER, macinfo->gldm_cookie);
/*
* Get the BNC/TP indicator from the conf file for 21040
*/
dnetp->bnc_indicator =
ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"bncaui", -1);
/*
* For 21140 check the data rate set in the conf file. Default is
* 100Mb/s. Disallow connections at settings that would conflict
* with what's in the conf file
*/
dnetp->speed =
ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
speed_propname, 0);
dnetp->full_duplex =
ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
duplex_propname, -1);
if (dnetp->speed == 100) {
dnetp->disallowed_media |= (1UL<<MEDIA_TP) | (1UL<<MEDIA_TP_FD);
} else if (dnetp->speed == 10) {
dnetp->disallowed_media |=
(1UL<<MEDIA_SYM_SCR) | (1UL<<MEDIA_SYM_SCR_FD);
}
if (dnetp->full_duplex == 1) {
dnetp->disallowed_media |=
(1UL<<MEDIA_TP) | (1UL<<MEDIA_SYM_SCR);
} else if (dnetp->full_duplex == 0) {
dnetp->disallowed_media |=
(1UL<<MEDIA_TP_FD) | (1UL<<MEDIA_SYM_SCR_FD);
}
if (dnetp->bnc_indicator == 0) /* Disable BNC and AUI media */
dnetp->disallowed_media |= (1UL<<MEDIA_BNC) | (1UL<<MEDIA_AUI);
else if (dnetp->bnc_indicator == 1) /* Force BNC only */
dnetp->disallowed_media = (uint32_t)~(1U<<MEDIA_BNC);
else if (dnetp->bnc_indicator == 2) /* Force AUI only */
dnetp->disallowed_media = (uint32_t)~(1U<<MEDIA_AUI);
dnet_reset_board(macinfo);
secondary = dnet_read_srom(devinfo, dnetp->board_type, dnetp->io_handle,
dnetp->io_reg, vendor_info, sizeof (vendor_info));
if (secondary == -1) /* ASSERT (vendor_info not big enough) */
goto fail1;
dnet_parse_srom(dnetp, &dnetp->sr, vendor_info);
if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
printsrom_propname, 0))
dnet_print_srom(&dnetp->sr);
dnetp->sr.netaddr[ETHERADDRL-1] += secondary; /* unique ether addr */
BCOPY((caddr_t)dnetp->sr.netaddr,
(caddr_t)dnetp->vendor_addr, ETHERADDRL);
BCOPY((caddr_t)dnetp->sr.netaddr,
(caddr_t)dnetp->curr_macaddr, ETHERADDRL);
/*
* determine whether to implement workaround from DEC
* for DMA overrun errata.
*/
dnetp->overrun_workaround =
((dnetp->board_type == DEVICE_ID_21140 && revid >= 0x20) ||
(dnetp->board_type == DEVICE_ID_21143 && revid <= 0x30)) ? 1 : 0;
dnetp->overrun_workaround =
ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
ofloprob_propname, dnetp->overrun_workaround);
/*
* Add the interrupt handler if dnet_hack_interrupts() returns 0.
* Otherwise dnet_hack_interrupts() itself adds the handler.
*/
if (!dnet_hack_interrupts(macinfo, secondary)) {
(void) ddi_add_intr(devinfo, 0, NULL,
NULL, gld_intr, (caddr_t)macinfo);
}
dnetp->max_tx_desc = max_tx_desc;
dnetp->max_rx_desc = max_rx_desc_21040;
if (dnetp->board_type != DEVICE_ID_21040 &&
dnetp->board_type != DEVICE_ID_21041 &&
dnetp->speed != 10)
dnetp->max_rx_desc = max_rx_desc_21140;
/* Allocate the TX and RX descriptors/buffers. */
if (dnet_alloc_bufs(macinfo) == FAILURE) {
cmn_err(CE_WARN, "DNET: Not enough DMA memory for buffers.");
goto fail2;
}
/*
* Register ourselves with the GLD interface
*
* gld_register will:
* link us with the GLD system;
* set our ddi_set_driver_private(9F) data to the macinfo pointer;
* save the devinfo pointer in macinfo->gldm_devinfo;
* map the registers, putting the kvaddr into macinfo->gldm_memp;
* add the interrupt, putting the cookie in gldm_cookie;
* init the gldm_intrlock mutex which will block that interrupt;
* create the minor node.
*/
if (gld_register(devinfo, "dnet", macinfo) == DDI_SUCCESS) {
mutex_enter(&dnetp->intrlock);
dnetp->phyaddr = -1;
if (dnetp->board_type == DEVICE_ID_21140 ||
dnetp->board_type == DEVICE_ID_21143)
do_phy(macinfo); /* Initialize the PHY, if any */
find_active_media(macinfo);
/* if the chosen media is non-MII, stop the port monitor */
if (dnetp->selected_media_block->media_code != MEDIA_MII &&
dnetp->mii != NULL) {
mii_destroy(dnetp->mii);
dnetp->mii = NULL;
dnetp->phyaddr = -1;
}
#ifdef DNETDEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "dnet: link configured : %s",
media_str[dnetp->selected_media_block->media_code]);
#endif
bzero(dnetp->setup_buf_vaddr, SETUPBUF_SIZE);
dnet_reset_board(macinfo);
dnet_init_board(macinfo);
/* XXX function return value ignored */
mutex_exit(&dnetp->intrlock);
/* dropped intrlock as dnet_stop_board() grabs intrlock */
(void) dnet_stop_board(macinfo);
return (DDI_SUCCESS);
}
fail2:
/* XXX function return value ignored */
/*
* dnet_detach_hacked_interrupt() will remove
* interrupt for the non-hacked case also.
*/
(void) dnet_detach_hacked_interrupt(devinfo);
dnet_free_bufs(macinfo);
fail1:
mutex_destroy(&dnetp->txlock);
mutex_destroy(&dnetp->intrlock);
fail:
ddi_regs_map_free(&dnetp->io_handle);
kmem_free(dnetp, sizeof (struct dnetinstance));
gld_mac_free(macinfo);
return (DDI_FAILURE);
}
/*
* detach(9E) -- Detach a device from the system
*/
static int
dnetdetach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
int32_t rc;
gld_mac_info_t *macinfo; /* GLD structure */
struct dnetinstance *dnetp; /* Our private device info */
int32_t proplen;
#ifdef DNETDEBUG
if (dnetdebug & DNETDDI)
cmn_err(CE_NOTE, "dnetdetach(0x%p)", (void *) devinfo);
#endif
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
/* Get the driver private (gld_mac_info_t) structure */
macinfo = ddi_get_driver_private(devinfo);
dnetp = (struct dnetinstance *)(macinfo->gldm_private);
/*
* Unregister ourselves from the GLD interface
*
* gld_unregister will:
* remove the minor node;
* unmap the registers;
* remove the interrupt;
* destroy the gldm_intrlock mutex;
* unlink us from the GLD system.
*/
if (gld_unregister(macinfo) != DDI_SUCCESS)
return (DDI_FAILURE);
/* stop the board if it is running */
dnet_reset_board(macinfo);
if ((rc = dnet_detach_hacked_interrupt(devinfo)) != DDI_SUCCESS)
return (rc);
if (dnetp->mii != NULL)
mii_destroy(dnetp->mii);
/* Free leaf information */
set_leaf(&dnetp->sr, NULL);
ddi_regs_map_free(&dnetp->io_handle);
dnet_free_bufs(macinfo);
mutex_destroy(&dnetp->txlock);
mutex_destroy(&dnetp->intrlock);
kmem_free(dnetp, sizeof (struct dnetinstance));
gld_mac_free(macinfo);
#ifdef BUG_4010796
if (ddi_getproplen(DDI_DEV_T_ANY, devinfo, 0,
"DNET_HACK", &proplen) != DDI_PROP_SUCCESS)
return (DDI_SUCCESS);
/*
* We must remove the properties we added, because if we leave
* them in the devinfo nodes and the driver is unloaded, when
* the driver is reloaded the info will still be there, causing
* nodes which had returned PROBE_PARTIAL the first time to
* instead return PROBE_SUCCESS, in turn causing the nodes to be
* attached in a different order, causing their PPA numbers to
* be different the second time around, which is undesirable.
*/
(void) ddi_prop_remove(DDI_DEV_T_NONE, devinfo, "DNET_HACK");
(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
"DNET_SROM");
(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
"DNET_DEVNUM");
#endif
return (DDI_SUCCESS);
}
/*
* ========== GLD Entry Points ==========
*/
/*
* dnet_reset() -- reset the board to initial state;
*/
static int
dnet_reset(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_reset(0x%p)", (void *) macinfo);
#endif
mutex_enter(&dnetp->intrlock);
/*
* Initialize internal data structures
*/
bzero(dnetp->setup_buf_vaddr, SETUPBUF_SIZE);
bzero(dnetp->multicast_cnt, MCASTBUF_SIZE);
dnetp->promisc = 0;
mutex_enter(&dnetp->txlock);
dnetp->need_saddr = 0;
mutex_exit(&dnetp->txlock);
dnet_reset_board(macinfo);
if (ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo, DDI_PROP_DONTPASS,
"reset_do_find_active_media", 0)) {
find_active_media(macinfo); /* Redetermine active media */
/* Go back to a good state */
bzero(dnetp->setup_buf_vaddr, SETUPBUF_SIZE);
dnet_reset_board(macinfo);
}
dnet_init_board(macinfo);
mutex_exit(&dnetp->intrlock);
return (0);
}
static void
dnet_reset_board(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
uint32_t val;
/*
* before initializing the dnet should be in STOP state
*/
/* XXX function return value ignored */
/* (void) dnet_stop_board(macinfo); */
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
val & ~(START_TRANSMIT | START_RECEIVE));
/*
* Reset the chip
*/
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, INT_MASK_REG), 0);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, BUS_MODE_REG), SW_RESET);
drv_usecwait(5);
}
/*
* dnet_init_board() -- initialize the specified network board short of
* actually starting the board. Call after dnet_reset_board().
* called with intrlock held.
*/
static void
dnet_init_board(gld_mac_info_t *macinfo)
{
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_init_board(0x%p)", (void *) macinfo);
#endif
set_opr(macinfo);
set_gpr(macinfo);
set_sia(macinfo);
dnet_chip_init(macinfo);
}
/* dnet_chip_init() - called with intrlock held */
static void
dnet_chip_init(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, BUS_MODE_REG),
CACHE_ALIGN | BURST_SIZE); /* CSR0 */
/*
* Initialize the TX and RX descriptors/buffers
*/
dnet_init_txrx_bufs(macinfo);
/*
* Set the base address of the Rx descriptor list in CSR3
*/
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, RX_BASE_ADDR_REG),
(uint32_t)(dnetp->rx_desc_paddr));
/*
* Set the base address of the Tx descrptor list in CSR4
*/
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, TX_BASE_ADDR_REG),
(uint32_t)(dnetp->tx_desc_paddr));
dnetp->tx_current_desc = dnetp->rx_current_desc = 0;
dnetp->transmitted_desc = 0;
dnetp->free_desc = dnetp->max_tx_desc;
enable_interrupts(dnetp, 1);
}
/*
* dnet_start() -- start the board receiving and allow transmits.
* Called with intrlock held.
*/
static int
dnet_start(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
uint32_t val;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_start(0x%p)", (void *) macinfo);
#endif
ASSERT(MUTEX_HELD(&dnetp->intrlock));
/*
* start the board and enable receiving
*/
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
val | START_TRANSMIT);
(void) dnet_set_addr(macinfo, dnetp->curr_macaddr);
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
val | START_RECEIVE);
enable_interrupts(dnetp, 1);
return (0);
}
/*
* dnet_start_board() -- start the board receiving and allow transmits.
*/
static int
dnet_start_board(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_start_board(0x%p)", (void *) macinfo);
#endif
mutex_enter(&dnetp->intrlock);
/*
* start the board and enable receiving
*/
(void) dnet_start(macinfo);
mutex_exit(&dnetp->intrlock);
return (0);
}
/*
* dnet_stop_board() -- stop board receiving
*/
static int
dnet_stop_board(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
uint32_t val;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_stop_board(0x%p)", (void *) macinfo);
#endif
/*
* stop the board and disable transmit/receive
*/
mutex_enter(&dnetp->intrlock);
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG),
val & ~(START_TRANSMIT | START_RECEIVE));
mutex_exit(&dnetp->intrlock);
return (0);
}
/*
* dnet_set_addr() -- set the physical network address on the board
* Called with intrlock held.
*/
static int
dnet_set_addr(gld_mac_info_t *macinfo, uchar_t *macaddr)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
struct tx_desc_type *desc;
int current_desc;
uint32_t index;
uint32_t *hashp;
uint32_t val;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_set_addr(0x%p)", (void *) macinfo);
#endif
bcopy(macaddr, dnetp->curr_macaddr, ETHERADDRL);
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
if (!(val & START_TRANSMIT))
return (0);
current_desc = dnetp->tx_current_desc;
desc = &dnetp->tx_desc[current_desc];
mutex_enter(&dnetp->txlock);
dnetp->need_saddr = 0;
mutex_exit(&dnetp->txlock);
if ((alloc_descriptor(macinfo)) == FAILURE) {
mutex_enter(&dnetp->txlock);
dnetp->need_saddr = 1;
mutex_exit(&dnetp->txlock);
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_WARN, "DNET saddr:alloc descriptor failure");
#endif
return (0);
}
desc->buffer1 = (uint32_t)(dnetp->setup_buf_paddr);
desc->buffer2 = (uint32_t)(0);
desc->desc1.buffer_size1 = SETUPBUF_SIZE;
desc->desc1.buffer_size2 = 0;
desc->desc1.setup_packet = 1;
desc->desc1.first_desc = 0;
desc->desc1.last_desc = 0;
desc->desc1.filter_type0 = 1;
desc->desc1.filter_type1 = 1;
desc->desc1.int_on_comp = 1;
/*
* As we are using Imperfect filtering, the broadcast address has to
* be set explicitly in the 512 bit hash table. Hence the index into
* the hash table is calculated and the bit set to enable reception
* of broadcast packets.
*
* We also use HASH_ONLY mode, without using the perfect filter for
* our station address, because there appears to be a bug in the
* 21140 where it fails to receive the specified perfect filter
* address.
*
* Since dlsdmult comes through here, it doesn't matter that the count
* is wrong for the two bits that correspond to the cases below. The
* worst that could happen is that we'd leave on a bit for an old
* macaddr, in the case where the macaddr gets changed, which is rare.
* Since filtering is imperfect, it is OK if that happens.
*/
hashp = (uint32_t *)dnetp->setup_buf_vaddr;
index = hashindex((uchar_t *)dnet_broadcastaddr);
hashp[ index / 16 ] |= 1 << (index % 16);
index = hashindex((uchar_t *)dnetp->curr_macaddr);
hashp[ index / 16 ] |= 1 << (index % 16);
desc->desc0.own = 1;
ddi_put8(dnetp->io_handle, REG8(dnetp->io_reg, TX_POLL_REG),
TX_POLL_DEMAND);
return (0);
}
/*
* dnet_set_mac_addr() -- set the physical network address on the board
*/
static int
dnet_set_mac_addr(gld_mac_info_t *macinfo, uchar_t *macaddr)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_set_mac_addr(0x%p)", (void *) macinfo);
#endif
mutex_enter(&dnetp->intrlock);
(void) dnet_set_addr(macinfo, macaddr);
mutex_exit(&dnetp->intrlock);
return (0);
}
/*
* dnet_set_multicast() -- set (enable) or disable a multicast address
*
* Program the hardware to enable/disable the multicast address
* in "mcast". Enable if "op" is non-zero, disable if zero.
*/
static int
dnet_set_multicast(gld_mac_info_t *macinfo, uchar_t *mcast, int op)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
uint32_t index;
uint32_t *hashp;
uint32_t retval;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_set_multicast(0x%p, %s)",
(void *) macinfo, op ? "ON" : "OFF");
#endif
mutex_enter(&dnetp->intrlock);
index = hashindex(mcast);
hashp = (uint32_t *)dnetp->setup_buf_vaddr;
if (op == GLD_MULTI_ENABLE) {
if (dnetp->multicast_cnt[index]++) {
mutex_exit(&dnetp->intrlock);
return (0);
}
hashp[ index / 16 ] |= 1 << (index % 16);
} else {
if (--dnetp->multicast_cnt[index]) {
mutex_exit(&dnetp->intrlock);
return (0);
}
hashp[ index / 16 ] &= ~ (1 << (index % 16));
}
retval = dnet_set_addr(macinfo, dnetp->curr_macaddr);
mutex_exit(&dnetp->intrlock);
return (retval);
}
/*
* A hashing function used for setting the
* node address or a multicast address
*/
static uint32_t
hashindex(uchar_t *address)
{
uint32_t crc = (uint32_t)HASH_CRC;
uint32_t const POLY = HASH_POLY;
uint32_t msb;
int32_t byteslength;
uint8_t currentbyte;
uint32_t index;
int32_t bit;
int32_t shift;
for (byteslength = 0; byteslength < ETHERADDRL; byteslength++) {
currentbyte = address[byteslength];
for (bit = 0; bit < 8; bit++) {
msb = crc >> 31;
crc <<= 1;
if (msb ^ (currentbyte & 1)) {
crc ^= POLY;
crc |= 0x00000001;
}
currentbyte >>= 1;
}
}
for (index = 0, bit = 23, shift = 8;
shift >= 0;
bit++, shift--) {
index |= (((crc >> bit) & 1) << shift);
}
return (index);
}
/*
* dnet_set_promiscuous() -- set or reset promiscuous mode on the board
*
* Program the hardware to enable/disable promiscuous mode.
* Enable if "on" is non-zero, disable if zero.
*/
static int
dnet_set_promiscuous(gld_mac_info_t *macinfo, int on)
{
struct dnetinstance *dnetp;
uint32_t val;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_set_promiscuous(0x%p, %s)",
(void *) macinfo, on ? "ON" : "OFF");
#endif
dnetp = (struct dnetinstance *)macinfo->gldm_private;
mutex_enter(&dnetp->intrlock);
if (dnetp->promisc == on) {
mutex_exit(&dnetp->intrlock);
return (SUCCESS);
}
dnetp->promisc = on;
val = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
if (on != GLD_MAC_PROMISC_NONE)
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG),
val | PROM_MODE);
else
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG),
val & (~PROM_MODE));
mutex_exit(&dnetp->intrlock);
return (DDI_SUCCESS);
}
/*
* dnet_get_stats() -- update statistics
*
* GLD calls this routine just before it reads the driver's statistics
* structure. If your board maintains statistics, this is the time to
* read them in and update the values in the structure. If the driver
* maintains statistics continuously, this routine need do nothing.
*/
static int
dnet_get_stats(gld_mac_info_t *macinfo, struct gld_stats *sp)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_get_stats(0x%p)", (void *) macinfo);
#endif
sp->glds_errrcv = dnetp->stat_errrcv;
sp->glds_overflow = dnetp->stat_overflow;
sp->glds_intr = dnetp->stat_intr;
sp->glds_defer = dnetp->stat_defer;
sp->glds_missed = dnetp->stat_missed;
sp->glds_norcvbuf = dnetp->stat_norcvbuf;
sp->glds_crc = dnetp->stat_crc;
sp->glds_short = dnetp->stat_short;
sp->glds_frame = dnetp->stat_frame;
sp->glds_errxmt = dnetp->stat_errxmt;
sp->glds_collisions = dnetp->stat_collisions;
sp->glds_xmtlatecoll = dnetp->stat_xmtlatecoll;
sp->glds_excoll = dnetp->stat_excoll;
sp->glds_underflow = dnetp->stat_underflow;
sp->glds_nocarrier = dnetp->stat_nocarrier;
/* stats from instance structure */
sp->glds_speed = dnetp->speed * 1000000;
sp->glds_duplex =
dnetp->full_duplex ? GLD_DUPLEX_FULL : GLD_DUPLEX_HALF;
return (DDI_SUCCESS);
}
/*
* dnet_send() -- send a packet
*
* Called when a packet is ready to be transmitted. A pointer to an
* M_DATA message that contains the packet is passed to this routine.
* The complete LLC header is contained in the message's first message
* block, and the remainder of the packet is contained within
* additional M_DATA message blocks linked to the first message block.
*/
#define NextTXIndex(index) (((index)+1) % dnetp->max_tx_desc)
#define PrevTXIndex(index) (((index)-1) < 0 ? dnetp->max_tx_desc - 1: (index)-1)
static int
dnet_send(gld_mac_info_t *macinfo, mblk_t *mp)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
struct tx_desc_type *ring = dnetp->tx_desc;
int mblen, totlen;
int index, end_index, start_index;
int avail;
int error;
int bufn;
int bp_count;
int retval;
mblk_t *bp;
uint32_t tx_interrupt_mask;
#ifdef DNETDEBUG
if (dnetdebug & DNETSEND)
cmn_err(CE_NOTE, "dnet_send(0x%p, 0x%p)",
(void *) macinfo, (void *) mp);
#endif
mutex_enter(&dnetp->txlock);
if (dnetp->need_saddr) {
/* XXX function return value ignored */
mutex_exit(&dnetp->txlock);
mutex_enter(&dnetp->intrlock);
(void) dnet_set_addr(macinfo, dnetp->curr_macaddr);
mutex_exit(&dnetp->intrlock);
mutex_enter(&dnetp->txlock);
}
/* reclaim any xmit descriptors completed */
dnet_reclaim_Tx_desc(macinfo);
/*
* Use the data buffers from the message and construct the
* scatter/gather list by calling ddi_dma_addr_bind_handle().
*/
error = bp_count = 0;
totlen = 0;
bp = mp;
bufn = 0;
index = start_index = dnetp->tx_current_desc;
avail = dnetp->free_desc;
while (bp != NULL) {
uint_t ncookies;
ddi_dma_cookie_t dma_cookie;
if (++bp_count > DNET_MAX_FRAG) {
#ifndef DNET_NOISY
(void) pullupmsg(bp, -1);
#else
if (pullupmsg(bp, -1))
cmn_err(CE_NOTE, "DNET: pulled up send msg");
else
cmn_err(CE_NOTE, "DNET: couldn't pullup send msg");
#endif
}
mblen = (int)(bp->b_wptr - bp->b_rptr);
if (!mblen) { /* skip zero-length message blocks */
bp = bp->b_cont;
continue;
}
retval = ddi_dma_addr_bind_handle(dnetp->dma_handle_tx, NULL,
(caddr_t)bp->b_rptr, mblen,
DDI_DMA_WRITE | DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
&dma_cookie, &ncookies);
switch (retval) {
case DDI_DMA_MAPPED:
break; /* everything's fine */
case DDI_DMA_NORESOURCES:
error = 1; /* allow retry by gld */
break;
case DDI_DMA_NOMAPPING:
case DDI_DMA_INUSE:
case DDI_DMA_TOOBIG:
default:
error = 2; /* error, no retry */
break;
}
/*
* we can use two cookies per descriptor (i.e buffer1 and
* buffer2) so we need at least (ncookies+1)/2 descriptors.
*/
if (((ncookies + 1) >> 1) > dnetp->free_desc) {
(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
error = 1;
break;
}
/* setup the descriptors for this data buffer */
while (ncookies) {
end_index = index;
if (bufn % 2) {
ring[index].buffer2 =
(uint32_t)dma_cookie.dmac_address;
ring[index].desc1.buffer_size2 =
dma_cookie.dmac_size;
index = NextTXIndex(index); /* goto next desc */
} else {
/* initialize the descriptor */
ASSERT(ring[index].desc0.own == 0);
*(uint32_t *)&ring[index].desc0 = 0;
*(uint32_t *)&ring[index].desc1 &=
DNET_END_OF_RING;
ring[index].buffer1 =
(uint32_t)dma_cookie.dmac_address;
ring[index].desc1.buffer_size1 =
dma_cookie.dmac_size;
ring[index].buffer2 = (uint32_t)(0);
dnetp->free_desc--;
ASSERT(dnetp->free_desc >= 0);
}
totlen += dma_cookie.dmac_size;
bufn++;
if (--ncookies)
ddi_dma_nextcookie(dnetp->dma_handle_tx,
&dma_cookie);
}
(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
bp = bp->b_cont;
}
if (error == 1) {
dnetp->stat_defer++;
dnetp->free_desc = avail;
dnetp->need_gld_sched = 1;
mutex_exit(&dnetp->txlock);
return (GLD_TX_RESEND);
} else if (error) {
dnetp->free_desc = avail;
mutex_exit(&dnetp->txlock);
freemsg(mp);
return (0); /* Drop packet, don't retry */
}
if (totlen > ETHERMAX) {
cmn_err(CE_WARN, "DNET: tried to send large %d packet", totlen);
dnetp->free_desc = avail;
mutex_exit(&dnetp->txlock);
freemsg(mp);
return (0); /* We don't want to repeat this attempt */
}
/*
* Remeber the message buffer pointer to do freemsg() at xmit
* interrupt time.
*/
dnetp->tx_msgbufp[end_index] = mp;
/*
* Now set the first/last buffer and own bits
* Since the 21040 looks for these bits set in the
* first buffer, work backwards in multiple buffers.
*/
ring[end_index].desc1.last_desc = 1;
ring[end_index].desc1.int_on_comp = 1;
for (index = end_index; index != start_index;
index = PrevTXIndex(index))
ring[index].desc0.own = 1;
ring[start_index].desc1.first_desc = 1;
ring[start_index].desc0.own = 1;
dnetp->tx_current_desc = NextTXIndex(end_index);
/*
* Safety check: make sure end-of-ring is set in last desc.
*/
ASSERT(ring[dnetp->max_tx_desc-1].desc1.end_of_ring != 0);
/*
* Enable xmit interrupt if we are running out of xmit descriptors
* or there are more packets on the queue waiting to be transmitted.
*/
#ifdef GLD_INTR_WAIT /* XXX This relies on new GLD changes */
if (dnetp->free_desc <= dnet_xmit_threshold)
tx_interrupt_mask = TX_INTERRUPT_MASK;
else
tx_interrupt_mask = (macinfo->gldm_GLD_flags & GLD_INTR_WAIT) ?
TX_INTERRUPT_MASK : 0;
#else
tx_interrupt_mask = TX_INTERRUPT_MASK;
#endif
mutex_exit(&dnetp->txlock);
mutex_enter(&dnetp->intrlock);
enable_interrupts(dnetp, tx_interrupt_mask);
/*
* Kick the transmitter
*/
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, TX_POLL_REG),
TX_POLL_DEMAND);
mutex_exit(&dnetp->intrlock);
return (GLD_TX_OK); /* successful transmit attempt */
}
/*
* dnetintr() -- interrupt from board to inform us that a receive or
* transmit has completed.
*/
static uint_t
dnetintr(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
uint32_t int_status;
uint32_t tx_interrupt_mask;
mutex_enter(&dnetp->intrlock);
#ifdef DNETDEBUG
if (dnetdebug & DNETINT)
cmn_err(CE_NOTE, "dnetintr(0x%p)", (void *)macinfo);
#endif
int_status = ddi_get32(dnetp->io_handle, REG32(dnetp->io_reg,
STATUS_REG));
/*
* If interrupt was not from this board
*/
if (!(int_status & (NORMAL_INTR_SUMM | ABNORMAL_INTR_SUMM))) {
mutex_exit(&dnetp->intrlock);
return (DDI_INTR_UNCLAIMED);
}
dnetp->stat_intr++;
if (int_status & GPTIMER_INTR) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, STATUS_REG), GPTIMER_INTR);
if (dnetp->timer.cb)
dnetp->timer.cb(dnetp);
else
cmn_err(CE_WARN, "dnet: unhandled timer interrupt");
}
if (int_status & TX_INTR) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, STATUS_REG), TX_INTR);
mutex_enter(&dnetp->txlock);
if (dnetp->need_gld_sched) {
mutex_exit(&dnetp->txlock);
mutex_exit(&dnetp->intrlock);
gld_sched(macinfo);
mutex_enter(&dnetp->intrlock);
mutex_enter(&dnetp->txlock);
dnetp->need_gld_sched = 0;
}
/* reclaim any xmit descriptors that are completed */
dnet_reclaim_Tx_desc(macinfo);
mutex_exit(&dnetp->txlock);
}
/*
* Check if receive interrupt bit is set
*/
if (int_status & RX_INTR) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, STATUS_REG), RX_INTR);
dnet_getp(macinfo);
}
if (int_status & ABNORMAL_INTR_SUMM) {
/*
* Check for system error
*/
if (int_status & SYS_ERR) {
if ((int_status & SYS_ERR_BITS) == MASTER_ABORT)
cmn_err(CE_WARN, "DNET: Bus Master Abort");
if ((int_status & SYS_ERR_BITS) == TARGET_ABORT)
cmn_err(CE_WARN, "DNET: Bus Target Abort");
if ((int_status & SYS_ERR_BITS) == PARITY_ERROR)
cmn_err(CE_WARN, "DNET: Parity error");
}
/*
* If the jabber has timed out then reset the chip
*/
if (int_status & TX_JABBER_TIMEOUT)
cmn_err(CE_WARN, "DNET: Jabber timeout.");
/*
* If an underflow has occurred, reset the chip
*/
if (int_status & TX_UNDERFLOW)
cmn_err(CE_WARN, "DNET: Tx Underflow.");
#ifdef DNETDEBUG
if (dnetdebug & DNETINT)
cmn_err(CE_NOTE, "Trying to reset...");
#endif
dnet_reset_board(macinfo);
dnet_init_board(macinfo);
/* XXX function return value ignored */
(void) dnet_start(macinfo);
}
/*
* Enable the interrupts. Enable xmit interrupt only if we are
* running out of free descriptors or if there are packets
* in the queue waiting to be transmitted.
*/
#ifdef GLD_INTR_WAIT /* XXX This relies on new GLD changes */
if (dnetp->free_desc <= dnet_xmit_threshold)
tx_interrupt_mask = TX_INTERRUPT_MASK;
else
tx_interrupt_mask = (macinfo->gldm_GLD_flags & GLD_INTR_WAIT) ?
TX_INTERRUPT_MASK : 0;
#else
tx_interrupt_mask = TX_INTERRUPT_MASK;
#endif
enable_interrupts(dnetp, tx_interrupt_mask);
mutex_exit(&dnetp->intrlock);
return (DDI_INTR_CLAIMED); /* Indicate it was our interrupt */
}
static void
dnet_getp(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
int packet_length, index;
mblk_t *mp;
caddr_t virtual_address;
struct rx_desc_type *desc = dnetp->rx_desc;
int marker = dnetp->rx_current_desc;
int misses;
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV)
cmn_err(CE_NOTE, "dnet_getp(0x%p)", (void *)macinfo);
#endif
if (!dnetp->overrun_workaround) {
/*
* If the workaround is not in place, we must still update
* the missed frame statistic from the on-chip counter.
*/
misses = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, MISSED_FRAME_REG));
dnetp->stat_missed += (misses & MISSED_FRAME_MASK);
}
/* While host owns the current descriptor */
while (!(desc[dnetp->rx_current_desc].desc0.own)) {
struct free_ptr *frp;
caddr_t newbuf;
struct rbuf_list *rp;
index = dnetp->rx_current_desc;
ASSERT(desc[index].desc0.first_desc != 0);
/*
* DMA overrun errata from DEC: avoid possible bus hangs
* and data corruption
*/
if (dnetp->overrun_workaround &&
marker == dnetp->rx_current_desc) {
int opn;
do {
marker = (marker+1) % dnetp->max_rx_desc;
} while (!(dnetp->rx_desc[marker].desc0.own) &&
marker != index);
misses = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, MISSED_FRAME_REG));
dnetp->stat_missed +=
(misses & MISSED_FRAME_MASK);
if (misses & OVERFLOW_COUNTER_MASK) {
/*
* Overflow(s) have occurred : stop receiver,
* and wait until in stopped state
*/
opn = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG),
opn & ~(START_RECEIVE));
do {
drv_usecwait(10);
} while ((ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, STATUS_REG)) &
RECEIVE_PROCESS_STATE) != 0);
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV)
cmn_err(CE_CONT, "^*");
#endif
/* Discard probably corrupt frames */
while (!(dnetp->rx_desc[index].desc0.own)) {
dnetp->rx_desc[index].desc0.own = 1;
index = (index+1) % dnetp->max_rx_desc;
dnetp->stat_missed++;
}
/* restart the receiver */
opn = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG));
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG),
opn | START_RECEIVE);
marker = dnetp->rx_current_desc = index;
continue;
}
/*
* At this point, we know that all packets before
* "marker" were received before a dma overrun occurred
*/
}
/*
* If we get an oversized packet it could span multiple
* descriptors. If this happens an error bit should be set.
*/
while (desc[index].desc0.last_desc == 0) {
index = (index + 1) % dnetp->max_rx_desc;
if (desc[index].desc0.own)
return; /* not done receiving large packet */
}
while (dnetp->rx_current_desc != index) {
desc[dnetp->rx_current_desc].desc0.own = 1;
dnetp->rx_current_desc =
(dnetp->rx_current_desc + 1) % dnetp->max_rx_desc;
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV)
cmn_err(CE_WARN, "dnet: received large packet");
#endif
}
packet_length = desc[index].desc0.frame_len;
/*
* Remove CRC from received data. This is an artefact of the
* 21x4x chip and should not be passed higher up the network
* stack.
*/
packet_length -= ETHERFCSL;
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV) {
if (packet_length > ETHERMAX)
cmn_err(CE_WARN, "dnet: large packet size %d",
packet_length);
}
#endif
/* get the virtual address of the packet received */
virtual_address =
dnetp->rx_buf_vaddr[index];
/*
* If no packet errors then do:
* 1. Allocate a new receive buffer so that we can
* use the current buffer as streams buffer to
* avoid bcopy.
* 2. If we got a new receive buffer then allocate
* an mblk using desballoc().
* 3. Otherwise use the mblk from allocb() and do
* the bcopy.
*/
frp = NULL;
rp = NULL;
newbuf = NULL;
mp = NULL;
if (!desc[index].desc0.err_summary) {
ASSERT(packet_length < rx_buf_size);
/*
* Allocate another receive buffer for this descriptor.
* If we fail to allocate then we do the normal bcopy.
*/
rp = dnet_rbuf_alloc(dnetp->devinfo, 0);
if (rp != NULL) {
newbuf = rp->rbuf_vaddr;
frp = kmem_zalloc(sizeof (*frp), KM_NOSLEEP);
if (frp != NULL) {
frp->free_rtn.free_func = dnet_freemsg_buf;
frp->free_rtn.free_arg = (char *)frp;
frp->buf = virtual_address;
mp = desballoc((uchar_t *)virtual_address,
packet_length, 0, &frp->free_rtn);
if (mp == NULL) {
kmem_free(frp, sizeof (*frp));
dnet_rbuf_free((caddr_t)newbuf);
frp = NULL;
newbuf = NULL;
}
}
}
if (mp == NULL) {
if (newbuf != NULL)
dnet_rbuf_free((caddr_t)newbuf);
mp = allocb(packet_length, 0);
}
}
if (desc[index].desc0.err_summary || (mp == NULL)) {
/* Update gld statistics */
if (desc[index].desc0.err_summary)
update_rx_stats(macinfo, index);
else
dnetp->stat_norcvbuf++;
/*
* Reset ownership of the descriptor.
*/
desc[index].desc0.own = 1;
dnetp->rx_current_desc =
(dnetp->rx_current_desc+1) % dnetp->max_rx_desc;
/* Demand receive polling by the chip */
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, RX_POLL_REG), RX_POLL_DEMAND);
continue;
}
if (newbuf != NULL) {
uint32_t end_paddr;
/* attach the new buffer to the rx descriptor */
dnetp->rx_buf_vaddr[index] = newbuf;
desc[index].buffer1 = (uint32_t)rp->rbuf_paddr;
desc[index].desc1.buffer_size1 = rx_buf_size;
desc[index].desc1.buffer_size2 = 0;
end_paddr = (uint32_t)rp->rbuf_endpaddr;
if ((desc[index].buffer1 & ~dnetp->pgmask) !=
(end_paddr & ~dnetp->pgmask)) {
/* discontiguous */
desc[index].buffer2 = end_paddr&~dnetp->pgmask;
desc[index].desc1.buffer_size2 =
(end_paddr & dnetp->pgmask) + 1;
desc[index].desc1.buffer_size1 =
rx_buf_size-desc[index].desc1.buffer_size2;
}
} else {
/* couldn't allocate another buffer; copy the data */
BCOPY((caddr_t)virtual_address, (caddr_t)mp->b_wptr,
packet_length);
}
mp->b_wptr += packet_length;
desc[dnetp->rx_current_desc].desc0.own = 1;
/*
* Increment receive desc index. This is for the scan of
* next packet
*/
dnetp->rx_current_desc =
(dnetp->rx_current_desc+1) % dnetp->max_rx_desc;
/* Demand polling by chip */
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, RX_POLL_REG), RX_POLL_DEMAND);
/* send the packet upstream */
mutex_exit(&dnetp->intrlock);
gld_recv(macinfo, mp);
mutex_enter(&dnetp->intrlock);
}
}
/*
* Function to update receive statistics
*/
static void
update_rx_stats(gld_mac_info_t *macinfo, int index)
{
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
struct rx_desc_type *descp = &(dnetp->rx_desc[index]);
/*
* Update gld statistics
*/
dnetp->stat_errrcv++;
if (descp->desc0.overflow) {
/* FIFO Overrun */
dnetp->stat_overflow++;
}
if (descp->desc0.collision) {
/*EMPTY*/
/* Late Colllision on receive */
/* no appropriate counter */
}
if (descp->desc0.crc) {
/* CRC Error */
dnetp->stat_crc++;
}
if (descp->desc0.runt_frame) {
/* Runt Error */
dnetp->stat_short++;
}
if (descp->desc0.desc_err) {
/*EMPTY*/
/* Not enough receive descriptors */
/* This condition is accounted in dnetintr() */
/* macinfo->gldm_stats.glds_missed++; */
}
if (descp->desc0.frame2long) {
dnetp->stat_frame++;
}
}
/*
* Function to update transmit statistics
*/
static void
update_tx_stats(gld_mac_info_t *macinfo, int index)
{
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
struct tx_desc_type *descp = &(dnetp->tx_desc[index]);
int fd;
media_block_t *block = dnetp->selected_media_block;
/* Update gld statistics */
dnetp->stat_errxmt++;
/* If we're in full-duplex don't count collisions or carrier loss. */
if (dnetp->mii_up) {
fd = dnetp->mii_duplex;
} else {
/* Rely on media code */
fd =
block->media_code == MEDIA_TP_FD ||
block->media_code == MEDIA_SYM_SCR_FD;
}
if (descp->desc0.collision_count && !fd) {
dnetp->stat_collisions +=
descp->desc0.collision_count;
}
if (descp->desc0.late_collision && !fd) {
dnetp->stat_xmtlatecoll++;
}
if (descp->desc0.excess_collision && !fd) {
dnetp->stat_excoll++;
}
if (descp->desc0.underflow) {
dnetp->stat_underflow++;
}
#if 0
if (descp->desc0.tx_jabber_to) {
/* no appropriate counter */
}
#endif
if (descp->desc0.carrier_loss && !fd) {
dnetp->stat_nocarrier++;
}
if (descp->desc0.no_carrier && !fd) {
dnetp->stat_nocarrier++;
}
}
/*
* ========== Media Selection Setup Routines ==========
*/
static void
write_gpr(struct dnetinstance *dnetp, uint32_t val)
{
#ifdef DEBUG
if (dnetdebug & DNETREGCFG)
cmn_err(CE_NOTE, "GPR: %x", val);
#endif
switch (dnetp->board_type) {
case DEVICE_ID_21143:
/* Set the correct bit for a control write */
if (val & GPR_CONTROL_WRITE)
val |= CWE_21143, val &= ~GPR_CONTROL_WRITE;
/* Write to upper half of CSR15 */
dnetp->gprsia = (dnetp->gprsia & 0xffff) | (val << 16);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_GENERAL_REG),
dnetp->gprsia);
break;
default:
/* Set the correct bit for a control write */
if (val & GPR_CONTROL_WRITE)
val |= CWE_21140, val &= ~GPR_CONTROL_WRITE;
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, GP_REG),
val);
break;
}
}
static uint32_t
read_gpr(struct dnetinstance *dnetp)
{
switch (dnetp->board_type) {
case DEVICE_ID_21143:
/* Read upper half of CSR15 */
return (ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_GENERAL_REG)) >> 16);
default:
return (ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, GP_REG)));
}
}
static void
set_gpr(gld_mac_info_t *macinfo)
{
uint32_t *sequence;
int len;
struct dnetinstance *dnetp = (struct dnetinstance *)
macinfo->gldm_private;
LEAF_FORMAT *leaf = &dnetp->sr.leaf[dnetp->leaf];
media_block_t *block = dnetp->selected_media_block;
int i;
if (ddi_getlongprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "gpr-sequence", (caddr_t)&sequence,
&len) == DDI_PROP_SUCCESS) {
for (i = 0; i < len / sizeof (uint32_t); i++)
write_gpr(dnetp, sequence[i]);
kmem_free(sequence, len);
} else {
/*
* Write the reset sequence if this is the first time this
* block has been selected.
*/
if (block->rstseqlen) {
for (i = 0; i < block->rstseqlen; i++)
write_gpr(dnetp, block->rstseq[i]);
/*
* XXX Legacy blocks do not have reset sequences, so the
* static blocks will never be modified by this
*/
block->rstseqlen = 0;
}
if (leaf->gpr)
write_gpr(dnetp, leaf->gpr | GPR_CONTROL_WRITE);
/* write GPR sequence each time */
for (i = 0; i < block->gprseqlen; i++)
write_gpr(dnetp, block->gprseq[i]);
}
/* This has possibly caused a PHY to reset. Let MII know */
if (dnetp->phyaddr != -1)
/* XXX function return value ignored */
(void) mii_sync(dnetp->mii, dnetp->phyaddr);
drv_usecwait(5);
}
/* set_opr() - must be called with intrlock held */
static void
set_opr(gld_mac_info_t *macinfo)
{
uint32_t fd, mb1, sf;
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
int opnmode_len;
uint32_t val;
media_block_t *block = dnetp->selected_media_block;
ASSERT(block);
/* Check for custom "opnmode_reg" property */
opnmode_len = sizeof (val);
if (ddi_prop_op(DDI_DEV_T_ANY, dnetp->devinfo,
PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "opnmode_reg",
(caddr_t)&val, &opnmode_len) != DDI_PROP_SUCCESS)
opnmode_len = 0;
/* Some bits exist only on 21140 and greater */
if (dnetp->board_type != DEVICE_ID_21040 &&
dnetp->board_type != DEVICE_ID_21041) {
mb1 = OPN_REG_MB1;
sf = STORE_AND_FORWARD;
} else {
mb1 = sf = 0;
mb1 = OPN_REG_MB1; /* Needed for 21040? */
}
if (opnmode_len) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG), val);
dnet_reset_board(macinfo);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, OPN_MODE_REG), val);
return;
}
/*
* Set each bit in CSR6 that we want
*/
/* Always want these bits set */
val = HASH_FILTERING | HASH_ONLY | TX_THRESHOLD_160 | mb1 | sf;
/* Promiscuous mode */
val |= dnetp->promisc ? PROM_MODE : 0;
/* Scrambler for SYM style media */
val |= ((block->command & CMD_SCR) && !dnetp->disable_scrambler) ?
SCRAMBLER_MODE : 0;
/* Full duplex */
if (dnetp->mii_up) {
fd = dnetp->mii_duplex;
} else {
/* Rely on media code */
fd =
block->media_code == MEDIA_TP_FD ||
block->media_code == MEDIA_SYM_SCR_FD;
}
/* Port select (and therefore, heartbeat disable) */
val |= block->command & CMD_PS ? (PORT_SELECT | HEARTBEAT_DISABLE) : 0;
/* PCS function */
val |= (block->command) & CMD_PCS ? PCS_FUNCTION : 0;
val |= fd ? FULL_DUPLEX : 0;
#ifdef DNETDEBUG
if (dnetdebug & DNETREGCFG)
cmn_err(CE_NOTE, "OPN: %x", val);
#endif
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG), val);
dnet_reset_board(macinfo);
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, OPN_MODE_REG), val);
}
static void
set_sia(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
media_block_t *block = dnetp->selected_media_block;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
if (block->type == 2) {
int sia_delay;
#ifdef DNETDEBUG
if (dnetdebug & DNETREGCFG)
cmn_err(CE_NOTE,
"SIA: CSR13: %x, CSR14: %x, CSR15: %x",
block->un.sia.csr13,
block->un.sia.csr14,
block->un.sia.csr15);
#endif
sia_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "sia-delay", 10000);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_CONNECT_REG), 0);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_TXRX_REG),
block->un.sia.csr14);
/*
* For '143, we need to write through a copy of the register
* to keep the GP half intact
*/
dnetp->gprsia = (dnetp->gprsia&0xffff0000)|block->un.sia.csr15;
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_GENERAL_REG),
dnetp->gprsia);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_CONNECT_REG),
block->un.sia.csr13);
drv_usecwait(sia_delay);
} else if (dnetp->board_type != DEVICE_ID_21140) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_CONNECT_REG), 0);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, SIA_TXRX_REG), 0);
}
}
/*
* ========== Buffer Management Routines ==========
*/
/*
* This function (re)allocates the receive and transmit buffers and
* descriptors. It can be called more than once per instance, though
* currently it is only called from attach. It should only be called
* while the device is reset.
*/
static int
dnet_alloc_bufs(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
int i;
size_t len;
int page_size;
int realloc = 0;
int nrecv_desc_old = 0;
ddi_dma_cookie_t cookie;
uint_t ncookies;
/*
* check if we are trying to reallocate with different xmit/recv
* descriptor ring sizes.
*/
if ((dnetp->tx_desc != NULL) &&
(dnetp->nxmit_desc != dnetp->max_tx_desc))
realloc = 1;
if ((dnetp->rx_desc != NULL) &&
(dnetp->nrecv_desc != dnetp->max_rx_desc))
realloc = 1;
/* free up the old buffers if we are reallocating them */
if (realloc) {
nrecv_desc_old = dnetp->nrecv_desc;
dnet_free_bufs(macinfo); /* free the old buffers */
}
if (dnetp->dma_handle == NULL)
if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
DDI_DMA_SLEEP, 0, &dnetp->dma_handle) != DDI_SUCCESS)
return (FAILURE);
if (dnetp->dma_handle_tx == NULL)
if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr_tx,
DDI_DMA_SLEEP, 0, &dnetp->dma_handle_tx) != DDI_SUCCESS)
return (FAILURE);
if (dnetp->dma_handle_txdesc == NULL)
if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
DDI_DMA_SLEEP, 0, &dnetp->dma_handle_txdesc) != DDI_SUCCESS)
return (FAILURE);
if (dnetp->dma_handle_setbuf == NULL)
if (ddi_dma_alloc_handle(dnetp->devinfo, &dma_attr,
DDI_DMA_SLEEP, 0, &dnetp->dma_handle_setbuf) != DDI_SUCCESS)
return (FAILURE);
page_size = ddi_ptob(dnetp->devinfo, 1);
for (i = page_size, len = 0; i > 1; len++)
i >>= 1;
dnetp->pgmask = page_size - 1;
dnetp->pgshft = len;
/* allocate setup buffer if necessary */
if (dnetp->setup_buf_vaddr == NULL) {
if (ddi_dma_mem_alloc(dnetp->dma_handle_setbuf,
SETUPBUF_SIZE, &accattr, DDI_DMA_STREAMING,
DDI_DMA_DONTWAIT, 0, (caddr_t *)&dnetp->setup_buf_vaddr,
&len, &dnetp->setup_buf_acchdl) != DDI_SUCCESS)
return (FAILURE);
if (ddi_dma_addr_bind_handle(dnetp->dma_handle_setbuf,
NULL, dnetp->setup_buf_vaddr, SETUPBUF_SIZE,
DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
return (FAILURE);
dnetp->setup_buf_paddr = (caddr_t)(uintptr_t)
cookie.dmac_laddress;
bzero(dnetp->setup_buf_vaddr, len);
}
/* allocate xmit descriptor array of size dnetp->max_tx_desc */
if (dnetp->tx_desc == NULL) {
if (ddi_dma_mem_alloc(dnetp->dma_handle_txdesc,
sizeof (struct tx_desc_type) * dnetp->max_tx_desc,
&accattr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
(caddr_t *)&dnetp->tx_desc, &len,
&dnetp->tx_desc_acchdl) != DDI_SUCCESS)
return (FAILURE);
if (ddi_dma_addr_bind_handle(dnetp->dma_handle_txdesc,
NULL, (caddr_t)dnetp->tx_desc,
sizeof (struct tx_desc_type) * dnetp->max_tx_desc,
DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
return (FAILURE);
dnetp->tx_desc_paddr = (caddr_t)(uintptr_t)
cookie.dmac_laddress;
bzero(dnetp->tx_desc, len);
dnetp->nxmit_desc = dnetp->max_tx_desc;
dnetp->tx_msgbufp =
kmem_zalloc(dnetp->max_tx_desc * sizeof (mblk_t **),
KM_SLEEP);
}
/* allocate receive descriptor array of size dnetp->max_rx_desc */
if (dnetp->rx_desc == NULL) {
int ndesc;
if (ddi_dma_mem_alloc(dnetp->dma_handle,
sizeof (struct rx_desc_type) * dnetp->max_rx_desc,
&accattr, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
(caddr_t *)&dnetp->rx_desc, &len,
&dnetp->rx_desc_acchdl) != DDI_SUCCESS)
return (FAILURE);
if (ddi_dma_addr_bind_handle(dnetp->dma_handle,
NULL, (caddr_t)dnetp->rx_desc,
sizeof (struct rx_desc_type) * dnetp->max_rx_desc,
DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP,
NULL, &cookie, &ncookies) != DDI_DMA_MAPPED)
return (FAILURE);
dnetp->rx_desc_paddr = (caddr_t)(uintptr_t)
cookie.dmac_laddress;
bzero(dnetp->rx_desc, len);
dnetp->nrecv_desc = dnetp->max_rx_desc;
dnetp->rx_buf_vaddr =
kmem_zalloc(dnetp->max_rx_desc * sizeof (caddr_t),
KM_SLEEP);
dnetp->rx_buf_paddr =
kmem_zalloc(dnetp->max_rx_desc * sizeof (caddr_t),
KM_SLEEP);
/*
* Allocate or add to the pool of receive buffers. The pool
* is shared among all instances of dnet.
*
* XXX NEEDSWORK
*
* We arbitrarily allocate twice as many receive buffers as
* receive descriptors because we use the buffers for streams
* messages to pass the packets up the stream. We should
* instead have initialized constants reflecting
* MAX_RX_BUF_2104x and MAX_RX_BUF_2114x, and we should also
* probably have a total maximum for the free pool, so that we
* don't get out of hand when someone puts in an 8-port board.
* The maximum for the entire pool should be the total number
* of descriptors for all attached instances together, plus the
* total maximum for the free pool. This maximum would only be
* reached after some number of instances allocate buffers:
* each instance would add (max_rx_buf-max_rx_desc) to the free
* pool.
*/
ndesc = dnetp->max_rx_desc - nrecv_desc_old;
if ((ndesc > 0) &&
(dnet_rbuf_init(dnetp->devinfo, ndesc * 2) != 0))
return (FAILURE);
for (i = 0; i < dnetp->max_rx_desc; i++) {
struct rbuf_list *rp;
rp = dnet_rbuf_alloc(dnetp->devinfo, 1);
if (rp == NULL)
return (FAILURE);
dnetp->rx_buf_vaddr[i] = rp->rbuf_vaddr;
}
}
return (SUCCESS);
}
/*
* free descriptors/buffers allocated for this device instance. This routine
* should only be called while the device is reset.
*/
static void
dnet_free_bufs(gld_mac_info_t *macinfo)
{
int i;
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
/* free up any xmit descriptors/buffers */
if (dnetp->tx_desc != NULL) {
ddi_dma_mem_free(&dnetp->tx_desc_acchdl);
dnetp->tx_desc = NULL;
/* we use streams buffers for DMA in xmit process */
if (dnetp->tx_msgbufp != NULL) {
/* free up any streams message buffers unclaimed */
for (i = 0; i < dnetp->nxmit_desc; i++) {
if (dnetp->tx_msgbufp[i] != NULL) {
freemsg(dnetp->tx_msgbufp[i]);
}
}
kmem_free(dnetp->tx_msgbufp,
dnetp->nxmit_desc * sizeof (mblk_t **));
dnetp->tx_msgbufp = NULL;
}
dnetp->nxmit_desc = 0;
}
/* free up any receive descriptors/buffers */
if (dnetp->rx_desc != NULL) {
ddi_dma_mem_free(&dnetp->rx_desc_acchdl);
dnetp->rx_desc = NULL;
if (dnetp->rx_buf_vaddr != NULL) {
/* free up the attached rbufs if any */
for (i = 0; i < dnetp->nrecv_desc; i++) {
if (dnetp->rx_buf_vaddr[i])
dnet_rbuf_free((caddr_t)dnetp->rx_buf_vaddr[i]);
}
kmem_free(dnetp->rx_buf_vaddr,
dnetp->nrecv_desc * sizeof (caddr_t));
kmem_free(dnetp->rx_buf_paddr,
dnetp->nrecv_desc * sizeof (caddr_t));
dnetp->rx_buf_vaddr = NULL;
dnetp->rx_buf_paddr = NULL;
}
dnetp->nrecv_desc = 0;
}
if (dnetp->setup_buf_vaddr != NULL) {
ddi_dma_mem_free(&dnetp->setup_buf_acchdl);
dnetp->setup_buf_vaddr = NULL;
}
if (dnetp->dma_handle != NULL) {
(void) ddi_dma_unbind_handle(dnetp->dma_handle);
ddi_dma_free_handle(&dnetp->dma_handle);
dnetp->dma_handle = NULL;
}
if (dnetp->dma_handle_tx != NULL) {
(void) ddi_dma_unbind_handle(dnetp->dma_handle_tx);
ddi_dma_free_handle(&dnetp->dma_handle_tx);
dnetp->dma_handle_tx = NULL;
}
if (dnetp->dma_handle_txdesc != NULL) {
(void) ddi_dma_unbind_handle(dnetp->dma_handle_txdesc);
ddi_dma_free_handle(&dnetp->dma_handle_txdesc);
dnetp->dma_handle_txdesc = NULL;
}
if (dnetp->dma_handle_setbuf != NULL) {
(void) ddi_dma_unbind_handle(dnetp->dma_handle_setbuf);
ddi_dma_free_handle(&dnetp->dma_handle_setbuf);
dnetp->dma_handle_setbuf = NULL;
}
}
/*
* Initialize transmit and receive descriptors.
*/
static void
dnet_init_txrx_bufs(gld_mac_info_t *macinfo)
{
int i;
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
/*
* Initilize all the Tx descriptors
*/
for (i = 0; i < dnetp->nxmit_desc; i++) {
/*
* We may be resetting the device due to errors,
* so free up any streams message buffer unclaimed.
*/
if (dnetp->tx_msgbufp[i] != NULL) {
freemsg(dnetp->tx_msgbufp[i]);
dnetp->tx_msgbufp[i] = NULL;
}
*(uint32_t *)&dnetp->tx_desc[i].desc0 = 0;
*(uint32_t *)&dnetp->tx_desc[i].desc1 = 0;
dnetp->tx_desc[i].buffer1 = (uint32_t)(0);
dnetp->tx_desc[i].buffer2 = (uint32_t)(0);
}
dnetp->tx_desc[i - 1].desc1.end_of_ring = 1;
/*
* Initialize the Rx descriptors
*/
for (i = 0; i < dnetp->nrecv_desc; i++) {
uint32_t end_paddr;
*(uint32_t *)&dnetp->rx_desc[i].desc0 = 0;
*(uint32_t *)&dnetp->rx_desc[i].desc1 = 0;
dnetp->rx_desc[i].desc0.own = 1;
dnetp->rx_desc[i].desc1.buffer_size1 = rx_buf_size;
dnetp->rx_desc[i].buffer1 =
(uint32_t)dnetp->rx_buf_paddr[i];
dnetp->rx_desc[i].buffer2 = (uint32_t)(0);
end_paddr =
(uint32_t)dnetp->rx_buf_paddr[i]+rx_buf_size-1;
if ((dnetp->rx_desc[i].buffer1 & ~dnetp->pgmask) !=
(end_paddr & ~dnetp->pgmask)) {
/* discontiguous */
dnetp->rx_desc[i].buffer2 = end_paddr&~dnetp->pgmask;
dnetp->rx_desc[i].desc1.buffer_size2 =
(end_paddr & dnetp->pgmask) + 1;
dnetp->rx_desc[i].desc1.buffer_size1 =
rx_buf_size-dnetp->rx_desc[i].desc1.buffer_size2;
}
}
dnetp->rx_desc[i - 1].desc1.end_of_ring = 1;
}
static int
alloc_descriptor(gld_mac_info_t *macinfo)
{
int index;
struct dnetinstance *dnetp = /* Our private device info */
(struct dnetinstance *)macinfo->gldm_private;
struct tx_desc_type *ring = dnetp->tx_desc;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
alloctop:
mutex_enter(&dnetp->txlock);
index = dnetp->tx_current_desc;
dnet_reclaim_Tx_desc(macinfo);
/* we do have free descriptors, right? */
if (dnetp->free_desc <= 0) {
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV)
cmn_err(CE_NOTE, "dnet: Ring buffer is full");
#endif
mutex_exit(&dnetp->txlock);
return (FAILURE);
}
/* sanity, make sure the next descriptor is free for use (should be) */
if (ring[index].desc0.own) {
#ifdef DNETDEBUG
if (dnetdebug & DNETRECV)
cmn_err(CE_WARN,
"dnet: next descriptor is not free for use");
#endif
mutex_exit(&dnetp->txlock);
return (FAILURE);
}
if (dnetp->need_saddr) {
mutex_exit(&dnetp->txlock);
/* XXX function return value ignored */
/* XXX function return value ignored */
(void) dnet_set_addr(macinfo, dnetp->curr_macaddr);
goto alloctop;
}
*(uint32_t *)&ring[index].desc0 = 0; /* init descs */
*(uint32_t *)&ring[index].desc1 &= DNET_END_OF_RING;
/* hardware will own this descriptor when poll activated */
dnetp->free_desc--;
/* point to next free descriptor to be used */
dnetp->tx_current_desc = NextTXIndex(index);
#ifdef DNET_NOISY
cmn_err(CE_WARN, "sfree 0x%x, transmitted 0x%x, tx_current 0x%x",
dnetp->free_desc, dnetp->transmitted_desc, dnetp->tx_current_desc);
#endif
mutex_exit(&dnetp->txlock);
return (SUCCESS);
}
/*
* dnet_reclaim_Tx_desc() - called with txlock held.
*/
static void
dnet_reclaim_Tx_desc(gld_mac_info_t *macinfo)
{
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
struct tx_desc_type *desc = dnetp->tx_desc;
int index;
ASSERT(MUTEX_HELD(&dnetp->txlock));
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet_reclaim_Tx_desc(0x%p)",
(void *) macinfo);
#endif
index = dnetp->transmitted_desc;
while (((dnetp->free_desc == 0) || (index != dnetp->tx_current_desc)) &&
!(desc[index].desc0.own)) {
/*
* Check for Tx Error that gets set
* in the last desc.
*/
if (desc[index].desc1.setup_packet == 0 &&
desc[index].desc1.last_desc &&
desc[index].desc0.err_summary)
update_tx_stats(macinfo, index);
/*
* If we have used the streams message buffer for this
* descriptor then free up the message now.
*/
if (dnetp->tx_msgbufp[index] != NULL) {
freemsg(dnetp->tx_msgbufp[index]);
dnetp->tx_msgbufp[index] = NULL;
}
dnetp->free_desc++;
index = (index+1) % dnetp->max_tx_desc;
}
dnetp->transmitted_desc = index;
}
/*
* Receive buffer allocation/freeing routines.
*
* There is a common pool of receive buffers shared by all dnet instances.
*
* XXX NEEDSWORK
*
* We arbitrarily allocate twice as many receive buffers as
* receive descriptors because we use the buffers for streams
* messages to pass the packets up the stream. We should
* instead have initialized constants reflecting
* MAX_RX_BUF_2104x and MAX_RX_BUF_2114x, and we should also
* probably have a total maximum for the free pool, so that we
* don't get out of hand when someone puts in an 8-port board.
* The maximum for the entire pool should be the total number
* of descriptors for all attached instances together, plus the
* total maximum for the free pool. This maximum would only be
* reached after some number of instances allocate buffers:
* each instance would add (max_rx_buf-max_rx_desc) to the free
* pool.
*/
static struct rbuf_list *rbuf_usedlist_head;
static struct rbuf_list *rbuf_freelist_head;
static struct rbuf_list *rbuf_usedlist_end; /* last buffer allocated */
static int rbuf_freebufs; /* no. of free buffers in the pool */
static int rbuf_pool_size; /* total no. of buffers in the pool */
/* initialize/add 'nbufs' buffers to the rbuf pool */
/* ARGSUSED */
static int
dnet_rbuf_init(dev_info_t *dip, int nbufs)
{
int i;
struct rbuf_list *rp;
ddi_dma_cookie_t cookie;
uint_t ncookies;
size_t len;
mutex_enter(&dnet_rbuf_lock);
/* allocate buffers and add them to the pool */
for (i = 0; i < nbufs; i++) {
/* allocate rbuf_list element */
rp = kmem_zalloc(sizeof (struct rbuf_list), KM_SLEEP);
if (ddi_dma_alloc_handle(dip, &dma_attr_rb, DDI_DMA_SLEEP,
0, &rp->rbuf_dmahdl) != DDI_SUCCESS)
goto fail_kfree;
/* allocate dma memory for the buffer */
if (ddi_dma_mem_alloc(rp->rbuf_dmahdl, rx_buf_size, &accattr,
DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
&rp->rbuf_vaddr, &len,
&rp->rbuf_acchdl) != DDI_SUCCESS)
goto fail_freehdl;
if (ddi_dma_addr_bind_handle(rp->rbuf_dmahdl, NULL,
rp->rbuf_vaddr, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
DDI_DMA_SLEEP, NULL, &cookie,
&ncookies) != DDI_DMA_MAPPED)
goto fail_free;
if (ncookies > 2)
goto fail_unbind;
if (ncookies == 1) {
rp->rbuf_endpaddr = (caddr_t)(uintptr_t)
(cookie.dmac_laddress + rx_buf_size - 1);
} else {
ddi_dma_nextcookie(rp->rbuf_dmahdl, &cookie);
rp->rbuf_endpaddr = (caddr_t)(uintptr_t)
(cookie.dmac_laddress + cookie.dmac_size - 1);
}
rp->rbuf_paddr = (caddr_t)(uintptr_t)cookie.dmac_laddress;
rp->rbuf_next = rbuf_freelist_head;
rbuf_freelist_head = rp;
rbuf_pool_size++;
rbuf_freebufs++;
}
mutex_exit(&dnet_rbuf_lock);
return (0);
fail_unbind:
(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
fail_free:
ddi_dma_mem_free(&rp->rbuf_acchdl);
fail_freehdl:
ddi_dma_free_handle(&rp->rbuf_dmahdl);
fail_kfree:
kmem_free(rp, sizeof (struct rbuf_list));
mutex_exit(&dnet_rbuf_lock);
return (-1);
}
/*
* Try to free up all the rbufs in the pool. Returns 0 if it frees up all
* buffers. The buffers in the used list are considered busy so these
* buffers are not freed.
*/
static int
dnet_rbuf_destroy()
{
struct rbuf_list *rp, *next;
mutex_enter(&dnet_rbuf_lock);
for (rp = rbuf_freelist_head; rp; rp = next) {
next = rp->rbuf_next;
ddi_dma_mem_free(&rp->rbuf_acchdl);
(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
kmem_free(rp, sizeof (struct rbuf_list));
rbuf_pool_size--;
rbuf_freebufs--;
}
rbuf_freelist_head = NULL;
if (rbuf_pool_size) { /* pool is still not empty */
mutex_exit(&dnet_rbuf_lock);
return (-1);
}
mutex_exit(&dnet_rbuf_lock);
return (0);
}
static struct rbuf_list *
dnet_rbuf_alloc(dev_info_t *dip, int cansleep)
{
struct rbuf_list *rp;
size_t len;
ddi_dma_cookie_t cookie;
uint_t ncookies;
mutex_enter(&dnet_rbuf_lock);
if (rbuf_freelist_head == NULL) {
if (!cansleep) {
mutex_exit(&dnet_rbuf_lock);
return (NULL);
}
/* allocate rbuf_list element */
rp = kmem_zalloc(sizeof (struct rbuf_list), KM_SLEEP);
if (ddi_dma_alloc_handle(dip, &dma_attr_rb, DDI_DMA_SLEEP,
0, &rp->rbuf_dmahdl) != DDI_SUCCESS)
goto fail_kfree;
/* allocate dma memory for the buffer */
if (ddi_dma_mem_alloc(rp->rbuf_dmahdl, rx_buf_size, &accattr,
DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0,
&rp->rbuf_vaddr, &len,
&rp->rbuf_acchdl) != DDI_SUCCESS)
goto fail_freehdl;
if (ddi_dma_addr_bind_handle(rp->rbuf_dmahdl, NULL,
rp->rbuf_vaddr, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
DDI_DMA_SLEEP, NULL, &cookie,
&ncookies) != DDI_DMA_MAPPED)
goto fail_free;
if (ncookies > 2)
goto fail_unbind;
if (ncookies == 1) {
rp->rbuf_endpaddr = (caddr_t)(uintptr_t)
(cookie.dmac_laddress + rx_buf_size - 1);
} else {
ddi_dma_nextcookie(rp->rbuf_dmahdl, &cookie);
rp->rbuf_endpaddr = (caddr_t)(uintptr_t)
(cookie.dmac_laddress + cookie.dmac_size - 1);
}
rp->rbuf_paddr = (caddr_t)(uintptr_t)cookie.dmac_laddress;
rbuf_freelist_head = rp;
rbuf_pool_size++;
rbuf_freebufs++;
}
/* take the buffer from the head of the free list */
rp = rbuf_freelist_head;
rbuf_freelist_head = rbuf_freelist_head->rbuf_next;
/* update the used list; put the entry at the end */
if (rbuf_usedlist_head == NULL)
rbuf_usedlist_head = rp;
else
rbuf_usedlist_end->rbuf_next = rp;
rp->rbuf_next = NULL;
rbuf_usedlist_end = rp;
rbuf_freebufs--;
mutex_exit(&dnet_rbuf_lock);
return (rp);
fail_unbind:
(void) ddi_dma_unbind_handle(rp->rbuf_dmahdl);
fail_free:
ddi_dma_mem_free(&rp->rbuf_acchdl);
fail_freehdl:
ddi_dma_free_handle(&rp->rbuf_dmahdl);
fail_kfree:
kmem_free(rp, sizeof (struct rbuf_list));
mutex_exit(&dnet_rbuf_lock);
return (NULL);
}
static void
dnet_rbuf_free(caddr_t vaddr)
{
struct rbuf_list *rp, *prev;
ASSERT(vaddr != NULL);
ASSERT(rbuf_usedlist_head != NULL);
mutex_enter(&dnet_rbuf_lock);
/* find the entry in the used list */
for (prev = rp = rbuf_usedlist_head; rp; rp = rp->rbuf_next) {
if (rp->rbuf_vaddr == vaddr)
break;
prev = rp;
}
if (rp == NULL) {
cmn_err(CE_WARN, "DNET: rbuf_free: bad addr 0x%p",
(void *)vaddr);
mutex_exit(&dnet_rbuf_lock);
return;
}
/* update the used list and put the buffer back in the free list */
if (rbuf_usedlist_head != rp) {
prev->rbuf_next = rp->rbuf_next;
if (rbuf_usedlist_end == rp)
rbuf_usedlist_end = prev;
} else {
rbuf_usedlist_head = rp->rbuf_next;
if (rbuf_usedlist_end == rp)
rbuf_usedlist_end = NULL;
}
rp->rbuf_next = rbuf_freelist_head;
rbuf_freelist_head = rp;
rbuf_freebufs++;
mutex_exit(&dnet_rbuf_lock);
}
/*
* Free the receive buffer used in a stream's message block allocated
* thru desballoc().
*/
static void
dnet_freemsg_buf(struct free_ptr *frp)
{
dnet_rbuf_free((caddr_t)frp->buf); /* buffer goes back to the pool */
kmem_free(frp, sizeof (*frp)); /* free up the free_rtn structure */
}
/*
* ========== SROM Read Routines ==========
*/
/*
* The following code gets the SROM information, either by reading it
* from the device or, failing that, by reading a property.
*/
static int
dnet_read_srom(dev_info_t *devinfo, int board_type, ddi_acc_handle_t io_handle,
int io_reg, uchar_t *vi, int maxlen)
{
int all_ones, zerocheck, i;
/*
* Load SROM into vendor_info
*/
if (board_type == DEVICE_ID_21040)
dnet_read21040addr(devinfo, io_handle, io_reg, vi, &maxlen);
else
/* 21041/21140 serial rom */
dnet_read21140srom(io_handle, io_reg, vi, maxlen);
/*
* If the dumpsrom property is present in the conf file, print
* the contents of the SROM to the console
*/
if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"dumpsrom", 0))
dnet_dumpbin("SROM", vi, 1, maxlen);
for (zerocheck = i = 0, all_ones = 0xff; i < maxlen; i++) {
zerocheck |= vi[i];
all_ones &= vi[i];
}
if (zerocheck == 0 || all_ones == 0xff) {
return (get_alternative_srom_image(devinfo, vi, maxlen));
} else {
#ifdef BUG_4010796
set_alternative_srom_image(devinfo, vi, maxlen);
#endif
return (0); /* Primary */
}
}
/*
* The function reads the ethernet address of the 21040 adapter
*/
static void
dnet_read21040addr(dev_info_t *dip, ddi_acc_handle_t io_handle, int io_reg,
uchar_t *addr, int *len)
{
uint32_t val;
int i;
/* No point reading more than the ethernet address */
*len = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, macoffset_propname, 0) + ETHERADDRL;
/* Reset ROM pointer */
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG), 0);
for (i = 0; i < *len; i++) {
do {
val = ddi_get32(io_handle,
REG32(io_reg, ETHER_ROM_REG));
} while (val & 0x80000000);
addr[i] = val & 0xFF;
}
}
#define drv_nsecwait(x) drv_usecwait(((x)+999)/1000) /* XXX */
/*
* The function reads the SROM of the 21140 adapter
*/
static void
dnet_read21140srom(ddi_acc_handle_t io_handle, int io_reg, uchar_t *addr,
int maxlen)
{
uint32_t i, j;
uint32_t dout;
uint16_t word;
uint8_t rom_addr;
uint8_t bit;
rom_addr = 0;
for (i = 0; i < maxlen; i += 2) {
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM);
drv_nsecwait(30);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP);
drv_nsecwait(50);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP);
drv_nsecwait(100);
/* command */
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
drv_nsecwait(150);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | DATA_IN | SEL_CLK);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | DATA_IN | SEL_CLK);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | DATA_IN);
drv_nsecwait(100);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP);
drv_nsecwait(150);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP);
drv_nsecwait(100);
/* Address */
for (j = HIGH_ADDRESS_BIT; j >= 1; j >>= 1) {
bit = (rom_addr & j) ? DATA_IN : 0;
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | bit);
drv_nsecwait(150);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | bit | SEL_CLK);
drv_nsecwait(250);
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | bit);
drv_nsecwait(100);
}
drv_nsecwait(150);
/* Data */
word = 0;
for (j = 0x8000; j >= 1; j >>= 1) {
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP | SEL_CLK);
drv_nsecwait(100);
dout = ddi_get32(io_handle,
REG32(io_reg, ETHER_ROM_REG));
drv_nsecwait(150);
if (dout & DATA_OUT)
word |= j;
ddi_put32(io_handle,
REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM | SEL_CHIP);
drv_nsecwait(250);
}
addr[i] = (word & 0x0000FF);
addr[i + 1] = (word >> 8);
rom_addr++;
ddi_put32(io_handle, REG32(io_reg, ETHER_ROM_REG),
READ_OP | SEL_ROM);
drv_nsecwait(100);
}
}
/*
* XXX NEEDSWORK
*
* Some lame multiport cards have only one SROM, which can be accessed
* only from the "first" 21x4x chip, whichever that one is. If we can't
* get at our SROM, we look for its contents in a property instead, which
* we rely on the bootstrap to have properly set.
* #ifdef BUG_4010796
* We also have a hack to try to set it ourselves, when the "first" port
* attaches, if it has not already been properly set. However, this method
* is not reliable, since it makes the unwarrented assumption that the
* "first" port will attach first.
* #endif
*/
static int
get_alternative_srom_image(dev_info_t *devinfo, uchar_t *vi, int len)
{
int l = len;
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"DNET_SROM", (caddr_t)vi, &len) != DDI_PROP_SUCCESS &&
(len = l) && ddi_getlongprop_buf(DDI_DEV_T_ANY,
ddi_get_parent(devinfo), DDI_PROP_DONTPASS, "DNET_SROM",
(caddr_t)vi, &len) != DDI_PROP_SUCCESS)
return (-1); /* Can't find it! */
/*
* The return value from this routine specifies which port number
* we are. The primary port is denoted port 0. On a QUAD card we
* should return 1, 2, and 3 from this routine. The return value
* is used to modify the ethernet address from the SROM data.
*/
#ifdef BUG_4010796
{
/*
* For the present, we remember the device number of our primary
* sibling and hope we and our other siblings are consecutively
* numbered up from there. In the future perhaps the bootstrap
* will pass us the necessary information telling us which physical
* port we really are.
*/
pci_regspec_t *assignp;
int assign_len;
int devnum;
int primary_devnum;
primary_devnum = ddi_getprop(DDI_DEV_T_ANY, devinfo, 0,
"DNET_DEVNUM", -1);
if (primary_devnum == -1)
return (1); /* XXX NEEDSWORK -- We have no better idea */
if ((ddi_getlongprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"assigned-addresses", (caddr_t)&assignp,
&assign_len)) != DDI_PROP_SUCCESS)
return (1); /* XXX NEEDSWORK -- We have no better idea */
devnum = PCI_REG_DEV_G(assignp->pci_phys_hi);
kmem_free(assignp, assign_len);
return (devnum - primary_devnum);
}
#else
return (1); /* XXX NEEDSWORK -- We have no better idea */
#endif
}
#ifdef BUG_4010796
static void
set_alternative_srom_image(dev_info_t *devinfo, uchar_t *vi, int len)
{
int proplen;
pci_regspec_t *assignp;
int assign_len;
int devnum;
if (ddi_getproplen(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"DNET_SROM", &proplen) == DDI_PROP_SUCCESS ||
ddi_getproplen(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
DDI_PROP_DONTPASS, "DNET_SROM", &proplen) == DDI_PROP_SUCCESS)
return; /* Already done! */
/* function return value ignored */
(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE,
ddi_get_parent(devinfo), "DNET_SROM", (uchar_t *)vi, len);
(void) ddi_prop_update_string(DDI_DEV_T_NONE, devinfo,
"DNET_HACK", "hack");
if ((ddi_getlongprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"assigned-addresses", (caddr_t)&assignp,
&assign_len)) == DDI_PROP_SUCCESS) {
devnum = PCI_REG_DEV_G(assignp->pci_phys_hi);
kmem_free(assignp, assign_len);
/* function return value ignored */
(void) ddi_prop_update_int(DDI_DEV_T_NONE,
ddi_get_parent(devinfo), "DNET_DEVNUM", devnum);
}
}
#endif
/*
* ========== SROM Parsing Routines ==========
*/
static int
check_srom_valid(uchar_t *vi)
{
int word, bit;
uint8_t crc;
uint16_t *wvi; /* word16 pointer to vendor info */
uint16_t bitval;
/* verify that the number of controllers on the card is within range */
if (vi[SROM_ADAPTER_CNT] < 1 || vi[SROM_ADAPTER_CNT] > MAX_ADAPTERS)
return (0);
/*
* version 1 and 3 of this card did not check the id block CRC value
* and this can't be changed without retesting every supported card
*
* however version 4 of the SROM can have this test applied
* without fear of breaking something that used to work.
* the CRC algorithm is taken from the Intel document
* "21x4 Serial ROM Format"
* version 4.09
* 3-Mar-1999
*/
switch (vi[SROM_VERSION]) {
case 1:
/* fallthru */
case 3:
return (
vi[SROM_MBZ] == 0 && /* must be zero */
vi[SROM_MBZ2] == 0 && /* must be zero */
vi[SROM_MBZ3] == 0); /* must be zero */
case 4:
wvi = (uint16_t *)vi;
crc = 0xff;
for (word = 0; word < 9; word++)
for (bit = 15; bit >= 0; bit--) {
if (word == 8 && bit == 7)
return (crc == vi[16]);
bitval = ((wvi[word] >> bit) & 1) ^ ((crc >> 7) & 1);
crc <<= 1;
if (bitval == 1) {
crc ^= 7;
}
}
default:
return (0);
}
}
/*
* ========== Active Media Determination Routines ==========
*/
/* This routine is also called for V3 Compact and extended type 0 SROMs */
static int
is_fdmedia(int media)
{
if (media == MEDIA_TP_FD || media == MEDIA_SYM_SCR_FD)
return (1);
else
return (0);
}
/*
* "Linkset" is used to merge media that use the same link test check. So,
* if the TP link is added to the linkset, so is the TP Full duplex link.
* Used to avoid checking the same link status twice.
*/
static void
linkset_add(uint32_t *set, int media)
{
if (media == MEDIA_TP_FD || media == MEDIA_TP)
*set |= (1UL<<MEDIA_TP_FD) | (1UL<<MEDIA_TP);
else if (media == MEDIA_SYM_SCR_FD || media == MEDIA_SYM_SCR)
*set |= (1UL<<MEDIA_SYM_SCR_FD) | (1UL<<MEDIA_SYM_SCR);
else *set |= 1UL<<media;
}
static int
linkset_isset(uint32_t linkset, int media)
{
return (((1UL<<media) & linkset) ? 1:0);
}
/*
* The following code detects which Media is connected for 21041/21140
* Expect to change this code to support new 21140 variants.
* find_active_media() - called with intrlock held.
*/
static void
find_active_media(gld_mac_info_t *macinfo)
{
int i;
media_block_t *block;
media_block_t *best_allowed = NULL;
media_block_t *hd_found = NULL;
media_block_t *fd_found = NULL;
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
LEAF_FORMAT *leaf = &dnetp->sr.leaf[dnetp->leaf];
uint32_t checked = 0, links_up = 0;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
#ifdef SROMDEBUG
cmn_err(CE_NOTE, "find_active_media 0x%x,0x%x", sr->version, macinfo);
#endif
dnetp->selected_media_block = leaf->default_block;
if (dnetp->phyaddr != -1) {
dnetp->selected_media_block = leaf->mii_block;
setup_block(macinfo);
if (ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "portmon", 1)) {
/* XXX return value ignored */
(void) mii_start_portmon(dnetp->mii, dnet_mii_link_cb,
&dnetp->intrlock);
/*
* If the port monitor detects the link is already
* up, there is no point going through the rest of the
* link sense
*/
if (dnetp->mii_up) {
return;
}
}
}
/*
* Media is searched for in order of Precedence. This DEC SROM spec
* tells us that the first media entry in the SROM is the lowest
* precedence and should be checked last. This is why we go to the last
* Media block and work back to the beginning.
*
* However, some older SROMs (Cogent EM110's etc.) have this the wrong
* way around. As a result, following the SROM spec would result in a
* 10 link being chosen over a 100 link if both media are available.
* So we continue trying the media until we have at least tried the
* DEFAULT media.
*/
/* Search for an active medium, and select it */
for (block = leaf->block + leaf->block_count - 1;
block >= leaf->block; block--) {
int media = block->media_code;
/* User settings disallow selection of this block */
if (dnetp->disallowed_media & (1UL<<media))
continue;
/* We may not be able to pick the default */
if (best_allowed == NULL || block == leaf->default_block)
best_allowed = block;
#ifdef DEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "Testing %s medium (block type %d)",
media_str[media], block->type);
#endif
dnetp->selected_media_block = block;
switch (block->type) {
case 2: /* SIA Media block: Best we can do is send a packet */
setup_block(macinfo);
if (send_test_packet(macinfo)) {
if (!is_fdmedia(media))
return;
if (!fd_found)
fd_found = block;
}
break;
/* SYM/SCR or TP block: Use the link-sense bits */
case 0:
if (!linkset_isset(checked, media)) {
linkset_add(&checked, media);
if (((media == MEDIA_BNC ||
media == MEDIA_AUI) &&
send_test_packet(macinfo)) ||
dnet_link_sense(macinfo))
linkset_add(&links_up, media);
}
if (linkset_isset(links_up, media)) {
/*
* Half Duplex is *always* the favoured media.
* Full Duplex can be set and forced via the
* conf file.
*/
if (!is_fdmedia(media) &&
dnetp->selected_media_block ==
leaf->default_block) {
/*
* Cogent cards have the media in
* opposite order to the spec.,
* this code forces the media test to
* keep going until the default media
* is tested.
*
* In Cogent case, 10, 10FD, 100FD, 100
* 100 is the default but 10 could have
* been detected and would have been
* chosen but now we force it through to
* 100.
*/
setup_block(macinfo);
return;
} else if (!is_fdmedia(media)) {
/*
* This allows all the others to work
* properly by remembering the media
* that works and not defaulting to
* a FD link.
*/
if (hd_found == NULL)
hd_found = block;
} else if (fd_found == NULL) {
/*
* No media have already been found
* so far, this is FD, it works so
* remember it and if no others are
* detected, use it.
*/
fd_found = block;
}
}
break;
/*
* MII block: May take up to a second or so to settle if
* setup causes a PHY reset
*/
case 1: case 3:
setup_block(macinfo);
for (i = 0; ; i++) {
if (mii_linkup(dnetp->mii, dnetp->phyaddr)) {
/* XXX function return value ignored */
(void) mii_getspeed(dnetp->mii,
dnetp->phyaddr,
&dnetp->mii_speed,
&dnetp->mii_duplex);
dnetp->mii_up = 1;
leaf->mii_block = block;
return;
}
if (i == 10)
break;
delay(drv_usectohz(150000));
}
dnetp->mii_up = 0;
break;
}
} /* for loop */
if (hd_found) {
dnetp->selected_media_block = hd_found;
} else if (fd_found) {
dnetp->selected_media_block = fd_found;
} else {
if (best_allowed == NULL)
best_allowed = leaf->default_block;
dnetp->selected_media_block = best_allowed;
cmn_err(CE_WARN, "!dnet: Default media selected\n");
}
setup_block(macinfo);
}
/*
* Do anything neccessary to select the selected_media_block.
* setup_block() - called with intrlock held.
*/
static void
setup_block(gld_mac_info_t *macinfo)
{
dnet_reset_board(macinfo);
dnet_init_board(macinfo);
/* XXX function return value ignored */
(void) dnet_start(macinfo);
}
/* dnet_link_sense() - called with intrlock held */
static int
dnet_link_sense(gld_mac_info_t *macinfo)
{
/*
* This routine makes use of the command word from the srom config.
* Details of the auto-sensing information contained in this can
* be found in the "Digital Semiconductor 21X4 Serial ROM Format v3.03"
* spec. Section 4.3.2.1, and 4.5.2.1.3
*/
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
media_block_t *block = dnetp->selected_media_block;
uint32_t link, status, mask, polarity;
int settletime, stabletime, waittime, upsamples;
int delay_100, delay_10;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
/* Don't autosense if the medium does not support it */
if (block->command & (1 << 15)) {
/* This should be the default block */
if (block->command & (1UL<<14))
dnetp->sr.leaf[dnetp->leaf].default_block = block;
return (0);
}
delay_100 = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "autosense-delay-100", 2000);
delay_10 = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "autosense-delay-10", 400);
/*
* Scrambler may need to be disabled for link sensing
* to work
*/
dnetp->disable_scrambler = 1;
setup_block(macinfo);
dnetp->disable_scrambler = 0;
if (block->media_code == MEDIA_TP || block->media_code == MEDIA_TP_FD)
settletime = delay_10;
else
settletime = delay_100;
stabletime = settletime / 4;
mask = 1 << ((block->command & CMD_MEDIABIT_MASK) >> 1);
polarity = block->command & CMD_POL ? 0xffffffff : 0;
for (waittime = 0, upsamples = 0;
waittime <= settletime + stabletime && upsamples < 8;
waittime += stabletime/8) {
delay(drv_usectohz(stabletime*1000 / 8));
status = read_gpr(dnetp);
link = (status^polarity) & mask;
if (link)
upsamples++;
else
upsamples = 0;
}
#ifdef DNETDEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "%s upsamples:%d stat:%x polarity:%x "
"mask:%x link:%x",
upsamples == 8 ? "UP":"DOWN",
upsamples, status, polarity, mask, link);
#endif
if (upsamples == 8)
return (1);
return (0);
}
static int
send_test_packet(gld_mac_info_t *macinfo)
{
int packet_delay;
struct dnetinstance *dnetp = (struct dnetinstance *)
(macinfo->gldm_private);
struct tx_desc_type *desc;
int bufindex;
int media_code = dnetp->selected_media_block->media_code;
uint32_t del;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
/*
* For a successful test packet, the card must have settled into
* its current setting. Almost all cards we've tested manage to
* do this with all media within 50ms. However, the SMC 8432
* requires 300ms to settle into BNC mode. We now only do this
* from attach, and we do sleeping delay() instead of drv_usecwait()
* so we hope this .2 second delay won't cause too much suffering.
* ALSO: with an autonegotiating hub, an aditional 1 second delay is
* required. This is done if the media type is TP
*/
if (media_code == MEDIA_TP || media_code == MEDIA_TP_FD) {
packet_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "test_packet_delay_tp", 1300000);
} else {
packet_delay = ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "test_packet_delay", 300000);
}
delay(drv_usectohz(packet_delay));
desc = dnetp->tx_desc;
bufindex = dnetp->tx_current_desc;
if (alloc_descriptor(macinfo) == FAILURE) {
cmn_err(CE_WARN, "DNET: send_test_packet: alloc_descriptor"
"failed");
return (0);
}
/*
* use setup buffer as the buffer for the test packet
* instead of allocating one.
*/
ASSERT(dnetp->setup_buf_vaddr != NULL);
/* Put something decent in dest address so we don't annoy other cards */
BCOPY((caddr_t)dnetp->curr_macaddr,
(caddr_t)dnetp->setup_buf_vaddr, ETHERADDRL);
BCOPY((caddr_t)dnetp->curr_macaddr,
(caddr_t)dnetp->setup_buf_vaddr+ETHERADDRL, ETHERADDRL);
desc[bufindex].buffer1 = (uint32_t)(dnetp->setup_buf_paddr);
desc[bufindex].desc1.buffer_size1 = SETUPBUF_SIZE;
desc[bufindex].buffer2 = (uint32_t)(0);
desc[bufindex].desc1.first_desc = 1;
desc[bufindex].desc1.last_desc = 1;
desc[bufindex].desc1.int_on_comp = 1;
desc[bufindex].desc0.own = 1;
ddi_put8(dnetp->io_handle, REG8(dnetp->io_reg, TX_POLL_REG),
TX_POLL_DEMAND);
/*
* Give enough time for the chip to transmit the packet
*/
#if 1
del = 1000;
while (desc[bufindex].desc0.own && --del)
drv_usecwait(10); /* quickly wait up to 10ms */
if (desc[bufindex].desc0.own)
delay(drv_usectohz(200000)); /* nicely wait a longer time */
#else
del = 0x10000;
while (desc[bufindex].desc0.own && --del)
drv_usecwait(10);
#endif
#ifdef DNETDEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "desc0 bits = %u, %u, %u, %u, %u, %u",
desc[bufindex].desc0.own,
desc[bufindex].desc0.err_summary,
desc[bufindex].desc0.carrier_loss,
desc[bufindex].desc0.no_carrier,
desc[bufindex].desc0.late_collision,
desc[bufindex].desc0.link_fail);
#endif
if (desc[bufindex].desc0.own) /* it shouldn't take this long, error */
return (0);
return (!desc[bufindex].desc0.err_summary);
}
/* enable_interrupts - called with intrlock held */
static void
enable_interrupts(struct dnetinstance *dnetp, int enable_xmit)
{
ASSERT(MUTEX_HELD(&dnetp->intrlock));
/* Don't enable interrupts if they have been forced off */
if (dnetp->interrupts_disabled)
return;
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, INT_MASK_REG),
NORMAL_INTR_MASK | ABNORMAL_INTR_MASK | TX_UNDERFLOW_MASK |
(enable_xmit ? TX_INTERRUPT_MASK : 0) |
(dnetp->timer.cb ? GPTIMER_INTR : 0) |
RX_INTERRUPT_MASK | SYSTEM_ERROR_MASK | TX_JABBER_MASK);
}
/*
* Some older multiport cards are non-PCI compliant in their interrupt routing.
* Second and subsequent devices are incorrectly configured by the BIOS
* (either in their ILINE configuration or the MP Configuration Table for PC+MP
* systems).
* The hack stops gldregister() registering the interrupt routine for the
* FIRST device on the adapter, and registers its own. It builds up a table
* of macinfo structures for each device, and the new interrupt routine
* calls gldintr for each of them.
* Known cards that suffer from this problem are:
* All Cogent multiport cards;
* Znyx 314;
* Znyx 315.
*
* XXX NEEDSWORK -- see comments above get_alternative_srom_image(). This
* hack relies on the fact that the offending cards will have only one SROM.
* It uses this fact to identify devices that are on the same multiport
* adapter, as opposed to multiple devices from the same vendor (as
* indicated by "secondary")
*/
static int
dnet_hack_interrupts(gld_mac_info_t *macinfo, int secondary)
{
int i;
struct hackintr_inf *hackintr_inf;
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
dev_info_t *devinfo = dnetp->devinfo;
uint32_t oui = 0; /* Organizationally Unique ID */
if (ddi_getprop(DDI_DEV_T_ANY, devinfo, DDI_PROP_DONTPASS,
"no_INTA_workaround", 0) != 0)
return (0);
for (i = 0; i < 3; i++)
oui = (oui << 8) | dnetp->vendor_addr[i];
/* Check wheather or not we need to implement the hack */
switch (oui) {
case ZNYX_ETHER:
/* Znyx multiport 21040 cards <<==>> ZX314 or ZX315 */
if (dnetp->board_type != DEVICE_ID_21040)
return (0);
break;
case COGENT_ETHER:
/* All known Cogent multiport cards */
break;
case ADAPTEC_ETHER:
/* Adaptec multiport cards */
break;
default:
/* Other cards work correctly */
return (0);
}
/* card is (probably) non-PCI compliant in its interrupt routing */
if (!secondary) {
/*
* If we have already registered a hacked interrupt, and
* this is also a 'primary' adapter, then this is NOT part of
* a multiport card, but a second card on the same PCI bus.
* BUGID: 4057747
*/
if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
DDI_PROP_DONTPASS, hackintr_propname, 0) != 0)
return (0);
/* ... Primary not part of a multiport device */
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_NOTE, "dnet: Implementing hardware "
"interrupt flaw workaround");
#endif
dnetp->hackintr_inf = hackintr_inf =
kmem_zalloc(sizeof (struct hackintr_inf), KM_SLEEP);
if (hackintr_inf == NULL)
goto fail;
hackintr_inf->macinfos[0] = macinfo;
hackintr_inf->devinfo = devinfo;
/*
* Add a property to allow successive attaches to find the
* table
*/
if (ddi_prop_update_byte_array(DDI_DEV_T_NONE,
ddi_get_parent(devinfo), hackintr_propname,
(uchar_t *)&dnetp->hackintr_inf,
sizeof (void *)) != DDI_PROP_SUCCESS)
goto fail;
/* Register our hacked interrupt routine */
if (ddi_add_intr(devinfo, 0, &macinfo->gldm_cookie, NULL,
(uint_t (*)(char *))dnet_hack_intr,
(caddr_t)hackintr_inf) != DDI_SUCCESS) {
/* XXX function return value ignored */
(void) ddi_prop_remove(DDI_DEV_T_NONE,
ddi_get_parent(devinfo),
hackintr_propname);
goto fail;
}
/*
* Mutex required to ensure interrupt routine has completed
* when detaching devices
*/
mutex_init(&hackintr_inf->lock, NULL, MUTEX_DRIVER,
macinfo->gldm_cookie);
/* Stop GLD registering an interrupt */
return (-1);
} else {
/* Add the macinfo for this secondary device to the table */
hackintr_inf = (struct hackintr_inf *)(uintptr_t)
ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
DDI_PROP_DONTPASS, hackintr_propname, 0);
if (hackintr_inf == NULL)
goto fail;
/* Find an empty slot */
for (i = 0; i < MAX_INST; i++)
if (hackintr_inf->macinfos[i] == NULL)
break;
/* More than 8 ports on adapter ?! */
if (i == MAX_INST)
goto fail;
hackintr_inf->macinfos[i] = macinfo;
/*
* Allow GLD to register a handler for this
* device. If the card is actually broken, as we suspect, this
* handler will never get called. However, by registering the
* interrupt handler, we can copy gracefully with new multiport
* Cogent cards that decide to fix the hardware problem
*/
return (0);
}
fail:
cmn_err(CE_WARN, "dnet: Could not work around hardware interrupt"
" routing problem");
return (0);
}
/*
* Call gld_intr for all adapters on a multiport card
*/
static uint_t
dnet_hack_intr(struct hackintr_inf *hackintr_inf)
{
int i;
int claimed = DDI_INTR_UNCLAIMED;
/* Stop detaches while processing interrupts */
mutex_enter(&hackintr_inf->lock);
for (i = 0; i < MAX_INST; i++) {
if (hackintr_inf->macinfos[i] &&
gld_intr(hackintr_inf->macinfos[i]) == DDI_INTR_CLAIMED)
claimed = DDI_INTR_CLAIMED;
}
mutex_exit(&hackintr_inf->lock);
return (claimed);
}
/*
* This removes the detaching device from the table procesed by the hacked
* interrupt routine. Because the interrupts from all devices come in to the
* same interrupt handler, ALL devices must stop interrupting once the
* primary device detaches. This isn't a problem at present, because all
* instances of a device are detached when the driver is unloaded.
*/
static int
dnet_detach_hacked_interrupt(dev_info_t *devinfo)
{
int i;
struct hackintr_inf *hackintr_inf;
gld_mac_info_t *mac, *macinfo = ddi_get_driver_private(devinfo);
hackintr_inf = (struct hackintr_inf *)(uintptr_t)
ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(devinfo),
DDI_PROP_DONTPASS, hackintr_propname, 0);
/*
* No hackintr_inf implies hack was not required or the primary has
* detached, and our interrupts are already disabled
*/
if (!hackintr_inf) {
/* remove the interrupt for the non-hacked case */
ddi_remove_intr(devinfo, 0, macinfo->gldm_cookie);
return (DDI_SUCCESS);
}
/* Remove this device from the handled table */
mutex_enter(&hackintr_inf->lock);
for (i = 0; i < MAX_INST; i++) {
if (hackintr_inf->macinfos[i] == macinfo) {
hackintr_inf->macinfos[i] = NULL;
break;
}
}
mutex_exit(&hackintr_inf->lock);
/* Not the primary card, we are done */
if (devinfo != hackintr_inf->devinfo)
return (DDI_SUCCESS);
/*
* This is the primary card. All remaining adapters on this device
* must have their interrupts disabled before we remove the handler
*/
for (i = 0; i < MAX_INST; i++) {
if ((mac = hackintr_inf->macinfos[i]) != NULL) {
struct dnetinstance *altdnetp =
(struct dnetinstance *)mac->gldm_private;
altdnetp->interrupts_disabled = 1;
ddi_put32(altdnetp->io_handle,
REG32(altdnetp->io_reg, INT_MASK_REG), 0);
}
}
/* It should now be safe to remove the interrupt handler */
ddi_remove_intr(devinfo, 0, macinfo->gldm_cookie);
mutex_destroy(&hackintr_inf->lock);
/* XXX function return value ignored */
(void) ddi_prop_remove(DDI_DEV_T_NONE, ddi_get_parent(devinfo),
hackintr_propname);
kmem_free(hackintr_inf, sizeof (struct hackintr_inf));
return (DDI_SUCCESS);
}
/*
* ========== PHY MII Routines ==========
*/
/* do_phy() - called with intrlock held */
static void
do_phy(gld_mac_info_t *macinfo)
{
dev_info_t *dip;
struct dnetinstance
*dnetp = (struct dnetinstance *)macinfo->gldm_private;
LEAF_FORMAT *leaf = dnetp->sr.leaf + dnetp->leaf;
media_block_t *block;
int phy;
dip = dnetp->devinfo;
/*
* Find and configure the PHY media block. If NO PHY blocks are
* found on the SROM, but a PHY device is present, we assume the card
* is a legacy device, and that there is ONLY a PHY interface on the
* card (ie, no BNC or AUI, and 10BaseT is implemented by the PHY
*/
for (block = leaf->block + leaf->block_count -1;
block >= leaf->block; block --) {
if (block->type == 3 || block->type == 1) {
leaf->mii_block = block;
break;
}
}
/*
* If no MII block, select default, and hope this configuration will
* allow the phy to be read/written if it is present
*/
dnetp->selected_media_block = leaf->mii_block ?
leaf->mii_block : leaf->default_block;
setup_block(macinfo);
/* XXX function return value ignored */
(void) mii_create(dip, dnet_mii_write, dnet_mii_read, &dnetp->mii);
/*
* We try PHY 0 LAST because it is less likely to be connected
*/
for (phy = 1; phy < 33; phy++)
if (mii_probe_phy(dnetp->mii, phy % 32) == MII_SUCCESS &&
mii_init_phy(dnetp->mii, phy % 32) == MII_SUCCESS) {
#ifdef DNETDEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "dnet: "
"PHY at address %d", phy % 32);
#endif
dnetp->phyaddr = phy % 32;
if (!leaf->mii_block) {
/* Legacy card, change the leaf node */
set_leaf(&dnetp->sr, &leaf_phylegacy);
}
return;
}
#ifdef DNETDEBUG
if (dnetdebug & DNETSENSE)
cmn_err(CE_NOTE, "dnet: No PHY found");
#endif
}
static ushort_t
dnet_mii_read(dev_info_t *dip, int phy_addr, int reg_num)
{
gld_mac_info_t *macinfo;
struct dnetinstance *dnetp;
uint32_t command_word;
uint32_t tmp;
uint32_t data = 0;
int i;
int bits_in_ushort = ((sizeof (ushort_t))*8);
int turned_around = 0;
macinfo = ddi_get_driver_private(dip);
dnetp = (struct dnetinstance *)macinfo->gldm_private;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
/* Write Preamble */
write_mii(dnetp, MII_PRE, 2*bits_in_ushort);
/* Prepare command word */
command_word = (uint32_t)phy_addr << MII_PHY_ADDR_ALIGN;
command_word |= (uint32_t)reg_num << MII_REG_ADDR_ALIGN;
command_word |= MII_READ_FRAME;
write_mii(dnetp, command_word, bits_in_ushort-2);
mii_tristate(dnetp);
/* Check that the PHY generated a zero bit the 2nd clock */
tmp = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG));
turned_around = (tmp & MII_DATA_IN) ? 0 : 1;
/* read data WORD */
for (i = 0; i < bits_in_ushort; i++) {
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG), MII_READ);
drv_usecwait(MII_DELAY);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG), MII_READ | MII_CLOCK);
drv_usecwait(MII_DELAY);
tmp = ddi_get32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG));
drv_usecwait(MII_DELAY);
data = (data << 1) | (tmp >> MII_DATA_IN_POSITION) & 0x0001;
}
mii_tristate(dnetp);
return (turned_around ? data: -1);
}
static void
dnet_mii_write(dev_info_t *dip, int phy_addr, int reg_num, int reg_dat)
{
gld_mac_info_t *macinfo;
struct dnetinstance *dnetp;
uint32_t command_word;
int bits_in_ushort = ((sizeof (ushort_t))*8);
macinfo = ddi_get_driver_private(dip);
dnetp = (struct dnetinstance *)macinfo->gldm_private;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
write_mii(dnetp, MII_PRE, 2*bits_in_ushort);
/* Prepare command word */
command_word = ((uint32_t)phy_addr << MII_PHY_ADDR_ALIGN);
command_word |= ((uint32_t)reg_num << MII_REG_ADDR_ALIGN);
command_word |= (MII_WRITE_FRAME | (uint32_t)reg_dat);
write_mii(dnetp, command_word, 2*bits_in_ushort);
mii_tristate(dnetp);
}
/*
* Write data size bits from mii_data to the MII control lines.
*/
static void
write_mii(struct dnetinstance *dnetp, uint32_t mii_data, int data_size)
{
int i;
uint32_t dbit;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
for (i = data_size; i > 0; i--) {
dbit = ((mii_data >>
(31 - MII_WRITE_DATA_POSITION)) & MII_WRITE_DATA);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG),
MII_WRITE | dbit);
drv_usecwait(MII_DELAY);
ddi_put32(dnetp->io_handle,
REG32(dnetp->io_reg, ETHER_ROM_REG),
MII_WRITE | MII_CLOCK | dbit);
drv_usecwait(MII_DELAY);
mii_data <<= 1;
}
}
/*
* Put the MDIO port in tri-state for the turn around bits
* in MII read and at end of MII management sequence.
*/
static void
mii_tristate(struct dnetinstance *dnetp)
{
ASSERT(MUTEX_HELD(&dnetp->intrlock));
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, ETHER_ROM_REG),
MII_WRITE_TS);
drv_usecwait(MII_DELAY);
ddi_put32(dnetp->io_handle, REG32(dnetp->io_reg, ETHER_ROM_REG),
MII_WRITE_TS | MII_CLOCK);
drv_usecwait(MII_DELAY);
}
static void
set_leaf(SROM_FORMAT *sr, LEAF_FORMAT *leaf)
{
if (sr->leaf && !sr->leaf->is_static)
kmem_free(sr->leaf, sr->adapters * sizeof (LEAF_FORMAT));
sr->leaf = leaf;
}
/*
* Callback from MII module. Makes sure that the CSR registers are
* configured properly if the PHY changes mode.
*/
/* ARGSUSED */
/* dnet_mii_link_cb - called with intrlock held */
static void
dnet_mii_link_cb(dev_info_t *dip, int phy, enum mii_phy_state state)
{
gld_mac_info_t *macinfo = ddi_get_driver_private(dip);
struct dnetinstance *dnetp =
(struct dnetinstance *)macinfo->gldm_private;
LEAF_FORMAT *leaf;
ASSERT(MUTEX_HELD(&dnetp->intrlock));
leaf = dnetp->sr.leaf + dnetp->leaf;
if (state == phy_state_linkup) {
dnetp->mii_up = 1;
/* XXX function return value ignored */
(void) mii_getspeed(dnetp->mii,
dnetp->phyaddr, &dnetp->mii_speed,
&dnetp->mii_duplex);
dnetp->selected_media_block = leaf->mii_block;
setup_block(macinfo);
} else {
/* NEEDSWORK: Probably can call find_active_media here */
dnetp->mii_up = 0;
if (leaf->default_block->media_code == MEDIA_MII)
dnetp->selected_media_block = leaf->default_block;
setup_block(macinfo);
}
}
/*
* SROM parsing routines.
* Refer to the Digital 3.03 SROM spec while reading this! (references refer
* to this document)
* Where possible ALL vendor specific changes should be localised here. The
* SROM data should be capable of describing any programmatic irregularities
* of DNET cards (via SIA or GP registers, in particular), so vendor specific
* code elsewhere should not be required
*/
static void
dnet_parse_srom(struct dnetinstance *dnetp, SROM_FORMAT *sr, uchar_t *vi)
{
uint32_t ether_mfg = 0;
int i;
uchar_t *p;
if (!ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, "no_sromconfig", 0))
dnetp->sr.init_from_srom = check_srom_valid(vi);
if (dnetp->sr.init_from_srom && dnetp->board_type != DEVICE_ID_21040) {
/* Section 2/3: General SROM Format/ ID Block */
p = vi+18;
sr->version = *p++;
sr->adapters = *p++;
sr->leaf =
kmem_zalloc(sr->adapters * sizeof (LEAF_FORMAT), KM_SLEEP);
for (i = 0; i < 6; i++)
sr->netaddr[i] = *p++;
for (i = 0; i < sr->adapters; i++) {
uchar_t devno = *p++;
uint16_t offset = *p++;
offset |= *p++ << 8;
sr->leaf[i].device_number = devno;
parse_controller_leaf(dnetp, sr->leaf+i, vi+offset);
}
/*
* 'Orrible hack for cogent cards. The 6911A board seems to
* have an incorrect SROM. (From the OEMDEMO program
* supplied by cogent, it seems that the ROM matches a setup
* or a board with a QSI or ICS PHY.
*/
for (i = 0; i < 3; i++)
ether_mfg = (ether_mfg << 8) | sr->netaddr[i];
if (ether_mfg == ADAPTEC_ETHER) {
static uint16_t cogent_gprseq[] = {0x821, 0};
switch (vi[COGENT_SROM_ID]) {
case COGENT_ANA6911A_C:
case COGENT_ANA6911AC_C:
#ifdef DNETDEBUG
if (dnetdebug & DNETTRACE)
cmn_err(CE_WARN,
"Suspected bad GPR sequence."
" Making a guess (821,0)");
#endif
/* XXX function return value ignored */
(void) ddi_prop_update_byte_array(
DDI_DEV_T_NONE, dnetp->devinfo,
"gpr-sequence", (uchar_t *)cogent_gprseq,
sizeof (cogent_gprseq));
break;
}
}
} else {
/*
* Adhoc SROM, check for some cards which need special handling
* Assume vendor info contains ether address in first six bytes
*/
uchar_t *mac = vi + ddi_getprop(DDI_DEV_T_ANY, dnetp->devinfo,
DDI_PROP_DONTPASS, macoffset_propname, 0);
for (i = 0; i < 6; i++)
sr->netaddr[i] = mac[i];
if (dnetp->board_type == DEVICE_ID_21140) {
for (i = 0; i < 3; i++)
ether_mfg = (ether_mfg << 8) | mac[i];
switch (ether_mfg) {
case ASANTE_ETHER:
dnetp->vendor_21140 = ASANTE_TYPE;
dnetp->vendor_revision = 0;
set_leaf(sr, &leaf_asante);
sr->adapters = 1;
break;
case COGENT_ETHER:
case ADAPTEC_ETHER:
dnetp->vendor_21140 = COGENT_EM_TYPE;
dnetp->vendor_revision =
vi[VENDOR_REVISION_OFFSET];
set_leaf(sr, &leaf_cogent_100);
sr->adapters = 1;
break;
default:
dnetp->vendor_21140 = DEFAULT_TYPE;
dnetp->vendor_revision = 0;
set_leaf(sr, &leaf_default_100);
sr->adapters = 1;
break;
}
} else if (dnetp->board_type == DEVICE_ID_21041) {
set_leaf(sr, &leaf_21041);
} else if (dnetp->board_type == DEVICE_ID_21040) {
set_leaf(sr, &leaf_21040);
}
}
}
/* Section 4.2, 4.3, 4.4, 4.5 */
static void
parse_controller_leaf(struct dnetinstance *dnetp, LEAF_FORMAT *leaf,
uchar_t *vi)
{
int i;
leaf->selected_contype = *vi++;
leaf->selected_contype |= *vi++ << 8;
if (dnetp->board_type == DEVICE_ID_21140) /* Sect. 4.3 */
leaf->gpr = *vi++;
leaf->block_count = *vi++;
if (leaf->block_count > MAX_MEDIA) {
cmn_err(CE_WARN, "dnet: Too many media in SROM!");
leaf->block_count = 1;
}
for (i = 0; i <= leaf->block_count; i++) {
vi = parse_media_block(dnetp, leaf->block + i, vi);
if (leaf->block[i].command & CMD_DEFAULT_MEDIUM)
leaf->default_block = leaf->block+i;
}
/* No explicit default block: use last in the ROM */
if (leaf->default_block == NULL)
leaf->default_block = leaf->block + leaf->block_count -1;
}
static uchar_t *
parse_media_block(struct dnetinstance *dnetp, media_block_t *block, uchar_t *vi)
{
int i;
/*
* There are three kinds of media block we need to worry about:
* The 21041 blocks.
* 21140 blocks from a version 1 SROM
* 2114[023] block from a version 3 SROM
*/
if (dnetp->board_type == DEVICE_ID_21041) {
/* Section 4.2 */
block->media_code = *vi & 0x3f;
block->type = 2;
if (*vi++ & 0x40) {
block->un.sia.csr13 = *vi++;
block->un.sia.csr13 |= *vi++ << 8;
block->un.sia.csr14 = *vi++;
block->un.sia.csr14 |= *vi++ << 8;
block->un.sia.csr15 = *vi++;
block->un.sia.csr15 |= *vi++ << 8;
} else {
/* No media data (csrs 13,14,15). Insert defaults */
switch (block->media_code) {
case MEDIA_TP:
block->un.sia.csr13 = 0xef01;
block->un.sia.csr14 = 0x7f3f;
block->un.sia.csr15 = 0x0008;
break;
case MEDIA_TP_FD:
block->un.sia.csr13 = 0xef01;
block->un.sia.csr14 = 0x7f3d;
block->un.sia.csr15 = 0x0008;
break;
case MEDIA_BNC:
block->un.sia.csr13 = 0xef09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x0006;
break;
case MEDIA_AUI:
block->un.sia.csr13 = 0xef09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x000e;
break;
}
}
} else if (*vi & 0x80) { /* Extended format: Section 4.3.2.2 */
int blocklen = *vi++ & 0x7f;
block->type = *vi++;
switch (block->type) {
case 0: /* "non-MII": Section 4.3.2.2.1 */
block->media_code = (*vi++) & 0x3f;
block->gprseqlen = 1;
block->gprseq[0] = *vi++;
block->command = *vi++;
block->command |= *vi++ << 8;
break;
case 1: /* MII/PHY: Section 4.3.2.2.2 */
block->command = CMD_PS;
block->media_code = MEDIA_MII;
/* This is whats needed in CSR6 */
block->un.mii.phy_num = *vi++;
block->gprseqlen = *vi++;
for (i = 0; i < block->gprseqlen; i++)
block->gprseq[i] = *vi++;
block->rstseqlen = *vi++;
for (i = 0; i < block->rstseqlen; i++)
block->rstseq[i] = *vi++;
block->un.mii.mediacaps = *vi++;
block->un.mii.mediacaps |= *vi++ << 8;
block->un.mii.nwayadvert = *vi++;
block->un.mii.nwayadvert |= *vi++ << 8;
block->un.mii.fdxmask = *vi++;
block->un.mii.fdxmask |= *vi++ << 8;
block->un.mii.ttmmask = *vi++;
block->un.mii.ttmmask |= *vi++ << 8;
break;
case 2: /* SIA Media: Section 4.4.2.1.1 */
block->media_code = *vi & 0x3f;
if (*vi++ & 0x40) {
block->un.sia.csr13 = *vi++;
block->un.sia.csr13 |= *vi++ << 8;
block->un.sia.csr14 = *vi++;
block->un.sia.csr14 |= *vi++ << 8;
block->un.sia.csr15 = *vi++;
block->un.sia.csr15 |= *vi++ << 8;
} else {
/*
* SIA values not provided by SROM; provide
* defaults. See appendix D of 2114[23] manuals.
*/
switch (block->media_code) {
case MEDIA_BNC:
block->un.sia.csr13 = 0x0009;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x0000;
break;
case MEDIA_AUI:
block->un.sia.csr13 = 0x0009;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x0008;
break;
case MEDIA_TP:
block->un.sia.csr13 = 0x0001;
block->un.sia.csr14 = 0x7f3f;
block->un.sia.csr15 = 0x0000;
break;
case MEDIA_TP_FD:
block->un.sia.csr13 = 0x0001;
block->un.sia.csr14 = 0x7f3d;
block->un.sia.csr15 = 0x0000;
break;
default:
block->un.sia.csr13 = 0x0000;
block->un.sia.csr14 = 0x0000;
block->un.sia.csr15 = 0x0000;
}
}
/* Treat GP control/data as a GPR sequence */
block->gprseqlen = 2;
block->gprseq[0] = *vi++;
block->gprseq[0] |= *vi++ << 8;
block->gprseq[0] |= GPR_CONTROL_WRITE;
block->gprseq[1] = *vi++;
block->gprseq[1] |= *vi++ << 8;
break;
case 3: /* MII/PHY : Section 4.4.2.1.2 */
block->command = CMD_PS;
block->media_code = MEDIA_MII;
block->un.mii.phy_num = *vi++;
block->gprseqlen = *vi++;
for (i = 0; i < block->gprseqlen; i++) {
block->gprseq[i] = *vi++;
block->gprseq[i] |= *vi++ << 8;
}
block->rstseqlen = *vi++;
for (i = 0; i < block->rstseqlen; i++) {
block->rstseq[i] = *vi++;
block->rstseq[i] |= *vi++ << 8;
}
block->un.mii.mediacaps = *vi++;
block->un.mii.mediacaps |= *vi++ << 8;
block->un.mii.nwayadvert = *vi++;
block->un.mii.nwayadvert |= *vi++ << 8;
block->un.mii.fdxmask = *vi++;
block->un.mii.fdxmask |= *vi++ << 8;
block->un.mii.ttmmask = *vi++;
block->un.mii.ttmmask |= *vi++ << 8;
block->un.mii.miiintr |= *vi++;
break;
case 4: /* SYM Media: 4.5.2.1.3 */
block->media_code = *vi++ & 0x3f;
/* Treat GP control and data as a GPR sequence */
block->gprseqlen = 2;
block->gprseq[0] = *vi++;
block->gprseq[0] |= *vi++ << 8;
block->gprseq[0] |= GPR_CONTROL_WRITE;
block->gprseq[1] = *vi++;
block->gprseq[1] |= *vi++ << 8;
block->command = *vi++;
block->command = *vi++ << 8;
break;
case 5: /* GPR reset sequence: Section 4.5.2.1.4 */
block->rstseqlen = *vi++;
for (i = 0; i < block->rstseqlen; i++)
block->rstseq[i] = *vi++;
break;
default: /* Unknown media block. Skip it. */
cmn_err(CE_WARN, "dnet: Unsupported SROM block.");
vi += blocklen;
break;
}
} else { /* Compact format (or V1 SROM): Section 4.3.2.1 */
block->type = 0;
block->media_code = *vi++ & 0x3f;
block->gprseqlen = 1;
block->gprseq[0] = *vi++;
block->command = *vi++;
block->command |= (*vi++) << 8;
}
return (vi);
}
/*
* An alternative to doing this would be to store the legacy ROMs in binary
* format in the conf file, and in read_srom, pick out the data. This would
* then allow the parser to continue on as normal. This makes it a little
* easier to read.
*/
static void
setup_legacy_blocks()
{
LEAF_FORMAT *leaf;
media_block_t *block;
/* Default FAKE SROM */
leaf = &leaf_default_100;
leaf->is_static = 1;
leaf->default_block = &leaf->block[3];
leaf->block_count = 4; /* 100 cards are highly unlikely to have BNC */
block = leaf->block;
block->media_code = MEDIA_TP_FD;
block->type = 0;
block->command = 0x8e; /* PCS, PS off, media sense: bit7, pol=1 */
block++;
block->media_code = MEDIA_TP;
block->type = 0;
block->command = 0x8e; /* PCS, PS off, media sense: bit7, pol=1 */
block++;
block->media_code = MEDIA_SYM_SCR_FD;
block->type = 0;
block->command = 0x6d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
block++;
block->media_code = MEDIA_SYM_SCR;
block->type = 0;
block->command = 0x406d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
/* COGENT FAKE SROM */
leaf = &leaf_cogent_100;
leaf->is_static = 1;
leaf->default_block = &leaf->block[4];
leaf->block_count = 5; /* 100TX, 100TX-FD, 10T 10T-FD, BNC */
block = leaf->block; /* BNC */
block->media_code = MEDIA_BNC;
block->type = 0;
block->command = 0x8000; /* No media sense, PCS, SCR, PS all off */
block->gprseqlen = 2;
block->rstseqlen = 0;
block->gprseq[0] = 0x13f;
block->gprseq[1] = 1;
block++;
block->media_code = MEDIA_TP_FD;
block->type = 0;
block->command = 0x8e; /* PCS, PS off, media sense: bit7, pol=1 */
block->gprseqlen = 2;
block->rstseqlen = 0;
block->gprseq[0] = 0x13f;
block->gprseq[1] = 0x26;
block++; /* 10BaseT */
block->media_code = MEDIA_TP;
block->type = 0;
block->command = 0x8e; /* PCS, PS off, media sense: bit7, pol=1 */
block->gprseqlen = 2;
block->rstseqlen = 0;
block->gprseq[0] = 0x13f;
block->gprseq[1] = 0x3e;
block++; /* 100BaseTX-FD */
block->media_code = MEDIA_SYM_SCR_FD;
block->type = 0;
block->command = 0x6d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
block->gprseqlen = 2;
block->rstseqlen = 0;
block->gprseq[0] = 0x13f;
block->gprseq[1] = 1;
block++; /* 100BaseTX */
block->media_code = MEDIA_SYM_SCR;
block->type = 0;
block->command = 0x406d; /* PCS, PS, SCR on, media sense: bit6, pol=0 */
block->gprseqlen = 2;
block->rstseqlen = 0;
block->gprseq[0] = 0x13f;
block->gprseq[1] = 1;
/* Generic legacy card with a PHY. */
leaf = &leaf_phylegacy;
leaf->block_count = 1;
leaf->mii_block = leaf->block;
leaf->default_block = &leaf->block[0];
leaf->is_static = 1;
block = leaf->block;
block->media_code = MEDIA_MII;
block->type = 1; /* MII Block type 1 */
block->command = 1; /* Port select */
block->gprseqlen = 0;
block->rstseqlen = 0;
/* ASANTE FAKE SROM */
leaf = &leaf_asante;
leaf->is_static = 1;
leaf->default_block = &leaf->block[0];
leaf->block_count = 1;
block = leaf->block;
block->media_code = MEDIA_MII;
block->type = 1; /* MII Block type 1 */
block->command = 1; /* Port select */
block->gprseqlen = 3;
block->rstseqlen = 0;
block->gprseq[0] = 0x180;
block->gprseq[1] = 0x80;
block->gprseq[2] = 0x0;
/* LEGACY 21041 card FAKE SROM */
leaf = &leaf_21041;
leaf->is_static = 1;
leaf->block_count = 4; /* SIA Blocks for TP, TPfd, BNC, AUI */
leaf->default_block = &leaf->block[3];
block = leaf->block;
block->media_code = MEDIA_AUI;
block->type = 2;
block->un.sia.csr13 = 0xef09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x000e;
block++;
block->media_code = MEDIA_TP_FD;
block->type = 2;
block->un.sia.csr13 = 0xef01;
block->un.sia.csr14 = 0x7f3d;
block->un.sia.csr15 = 0x0008;
block++;
block->media_code = MEDIA_BNC;
block->type = 2;
block->un.sia.csr13 = 0xef09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x0006;
block++;
block->media_code = MEDIA_TP;
block->type = 2;
block->un.sia.csr13 = 0xef01;
block->un.sia.csr14 = 0x7f3f;
block->un.sia.csr15 = 0x0008;
/* LEGACY 21040 card FAKE SROM */
leaf = &leaf_21040;
leaf->is_static = 1;
leaf->block_count = 4; /* SIA Blocks for TP, TPfd, BNC, AUI */
block = leaf->block;
block->media_code = MEDIA_AUI;
block->type = 2;
block->un.sia.csr13 = 0x8f09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x000e;
block++;
block->media_code = MEDIA_TP_FD;
block->type = 2;
block->un.sia.csr13 = 0x0f01;
block->un.sia.csr14 = 0x7f3d;
block->un.sia.csr15 = 0x0008;
block++;
block->media_code = MEDIA_BNC;
block->type = 2;
block->un.sia.csr13 = 0xef09;
block->un.sia.csr14 = 0x0705;
block->un.sia.csr15 = 0x0006;
block++;
block->media_code = MEDIA_TP;
block->type = 2;
block->un.sia.csr13 = 0x8f01;
block->un.sia.csr14 = 0x7f3f;
block->un.sia.csr15 = 0x0008;
}
static void
dnet_print_srom(SROM_FORMAT *sr)
{
int i;
uchar_t *a = sr->netaddr;
cmn_err(CE_NOTE, "SROM Dump: %d. ver %d, Num adapters %d,"
"Addr:%x:%x:%x:%x:%x:%x",
sr->init_from_srom, sr->version, sr->adapters,
a[0], a[1], a[2], a[3], a[4], a[5]);
for (i = 0; i < sr->adapters; i++)
dnet_dump_leaf(sr->leaf+i);
}
static void
dnet_dump_leaf(LEAF_FORMAT *leaf)
{
int i;
cmn_err(CE_NOTE, "Leaf: Device %d, block_count %d, gpr: %x",
leaf->device_number, leaf->block_count, leaf->gpr);
for (i = 0; i < leaf->block_count; i++)
dnet_dump_block(leaf->block+i);
}
static void
dnet_dump_block(media_block_t *block)
{
cmn_err(CE_NOTE, "Block(%p): type %x, media %s, command: %x ",
(void *)block,
block->type, media_str[block->media_code], block->command);
dnet_dumpbin("\tGPR Seq", (uchar_t *)block->gprseq, 2,
block->gprseqlen *2);
dnet_dumpbin("\tGPR Reset", (uchar_t *)block->rstseq, 2,
block->rstseqlen *2);
switch (block->type) {
case 1: case 3:
cmn_err(CE_NOTE, "\tMII Info: phy %d, nway %x, fdx"
"%x, ttm %x, mediacap %x",
block->un.mii.phy_num, block->un.mii.nwayadvert,
block->un.mii.fdxmask, block->un.mii.ttmmask,
block->un.mii.mediacaps);
break;
case 2:
cmn_err(CE_NOTE, "\tSIA Regs: CSR13:%x, CSR14:%x, CSR15:%x",
block->un.sia.csr13, block->un.sia.csr14,
block->un.sia.csr15);
break;
}
}
/* Utility to print out binary info dumps. Handy for SROMs, etc */
static int
hexcode(unsigned val)
{
if (val <= 9)
return (val +'0');
if (val <= 15)
return (val + 'a' - 10);
return (-1);
}
static void
dnet_dumpbin(char *msg, unsigned char *data, int size, int len)
{
char hex[128], *p = hex;
char ascii[128], *q = ascii;
int i, j;
if (!len)
return;
for (i = 0; i < len; i += size) {
for (j = size - 1; j >= 0; j--) { /* PORTABILITY: byte order */
*p++ = hexcode(data[i+j] >> 4);
*p++ = hexcode(data[i+j] & 0xf);
*q++ = (data[i+j] < 32 || data[i+j] > 127) ?
'.' : data[i];
}
*p++ = ' ';
if (q-ascii >= 8) {
*p = *q = 0;
cmn_err(CE_NOTE, "%s: %s\t%s", msg, hex, ascii);
p = hex;
q = ascii;
}
}
if (p != hex) {
while ((p - hex) < 8*3)
*p++ = ' ';
*p = *q = 0;
cmn_err(CE_NOTE, "%s: %s\t%s", msg, hex, ascii);
}
}
#ifdef DNETDEBUG
void
dnet_usectimeout(struct dnetinstance *dnetp, uint32_t usecs, int contin,
timercb_t cback)
{
mutex_enter(&dnetp->intrlock);
dnetp->timer.start_ticks = (usecs * 100) / 8192;
dnetp->timer.cb = cback;
outl(dnetp->io_reg + GP_TIMER_REG, dnetp->timer.start_ticks |
(contin ? GPTIMER_CONT : 0));
if (dnetp->timer.cb)
enable_interrupts(dnetp, 1);
mutex_exit(&dnetp->intrlock);
}
uint32_t
dnet_usecelapsed(struct dnetinstance *dnetp)
{
uint32_t ticks = dnetp->timer.start_ticks -
(inl(dnetp->io_reg + GP_TIMER_REG) & 0xffff);
return ((ticks * 8192) / 100);
}
/* ARGSUSED */
void
dnet_timestamp(struct dnetinstance *dnetp, char *buf)
{
uint32_t elapsed = dnet_usecelapsed(dnetp);
char loc[32], *p = loc;
int firstdigit = 1;
uint32_t divisor;
while (*p++ = *buf++)
;
p--;
for (divisor = 1000000000; divisor /= 10; ) {
int digit = (elapsed / divisor);
elapsed -= digit * divisor;
if (!firstdigit || digit) {
*p++ = digit + '0';
firstdigit = 0;
}
}
/* Actual zero, output it */
if (firstdigit)
*p++ = '0';
*p++ = '-';
*p++ = '>';
*p++ = 0;
printf(loc);
dnet_usectimeout(dnetp, 1000000, 0, 0);
}
#endif