15181Sgd78059 /*
25181Sgd78059 * CDDL HEADER START
35181Sgd78059 *
45181Sgd78059 * The contents of this file are subject to the terms of the
55181Sgd78059 * Common Development and Distribution License (the "License").
65181Sgd78059 * You may not use this file except in compliance with the License.
75181Sgd78059 *
85181Sgd78059 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95181Sgd78059 * or http://www.opensolaris.org/os/licensing.
105181Sgd78059 * See the License for the specific language governing permissions
115181Sgd78059 * and limitations under the License.
125181Sgd78059 *
135181Sgd78059 * When distributing Covered Code, include this CDDL HEADER in each
145181Sgd78059 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155181Sgd78059 * If applicable, add the following below this CDDL HEADER, with the
165181Sgd78059 * fields enclosed by brackets "[]" replaced with your own identifying
175181Sgd78059 * information: Portions Copyright [yyyy] [name of copyright owner]
185181Sgd78059 *
195181Sgd78059 * CDDL HEADER END
205181Sgd78059 */
215181Sgd78059 /*
22*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
235181Sgd78059 * Use is subject to license terms.
245181Sgd78059 */
255181Sgd78059
265181Sgd78059
275181Sgd78059 #include <sys/types.h>
285181Sgd78059 #include <sys/sunddi.h>
299860Sgdamore@opensolaris.org #include <sys/policy.h>
309860Sgdamore@opensolaris.org #include <sys/sdt.h>
315181Sgd78059 #include "dmfe_impl.h"
325181Sgd78059
335181Sgd78059 /*
345181Sgd78059 * This is the string displayed by modinfo, etc.
355181Sgd78059 */
365181Sgd78059 static char dmfe_ident[] = "Davicom DM9102 Ethernet";
375181Sgd78059
385181Sgd78059
395181Sgd78059 /*
405181Sgd78059 * NOTES:
415181Sgd78059 *
425181Sgd78059 * #defines:
435181Sgd78059 *
445181Sgd78059 * DMFE_PCI_RNUMBER is the register-set number to use for the operating
455181Sgd78059 * registers. On an OBP-based machine, regset 0 refers to CONFIG space,
465181Sgd78059 * regset 1 will be the operating registers in I/O space, and regset 2
475181Sgd78059 * will be the operating registers in MEMORY space (preferred). If an
485181Sgd78059 * expansion ROM is fitted, it may appear as a further register set.
495181Sgd78059 *
505181Sgd78059 * DMFE_SLOP defines the amount by which the chip may read beyond
515181Sgd78059 * the end of a buffer or descriptor, apparently 6-8 dwords :(
525181Sgd78059 * We have to make sure this doesn't cause it to access unallocated
535181Sgd78059 * or unmapped memory.
545181Sgd78059 *
555181Sgd78059 * DMFE_BUF_SIZE must be at least (ETHERMAX + ETHERFCSL + DMFE_SLOP)
565181Sgd78059 * rounded up to a multiple of 4. Here we choose a power of two for
575181Sgd78059 * speed & simplicity at the cost of a bit more memory.
585181Sgd78059 *
595181Sgd78059 * However, the buffer length field in the TX/RX descriptors is only
605181Sgd78059 * eleven bits, so even though we allocate DMFE_BUF_SIZE (2048) bytes
615181Sgd78059 * per buffer, we tell the chip that they're only DMFE_BUF_SIZE_1
625181Sgd78059 * (2000) bytes each.
635181Sgd78059 *
645181Sgd78059 * DMFE_DMA_MODE defines the mode (STREAMING/CONSISTENT) used for
655181Sgd78059 * the data buffers. The descriptors are always set up in CONSISTENT
665181Sgd78059 * mode.
675181Sgd78059 *
685181Sgd78059 * DMFE_HEADROOM defines how much space we'll leave in allocated
695181Sgd78059 * mblks before the first valid data byte. This should be chosen
705181Sgd78059 * to be 2 modulo 4, so that once the ethernet header (14 bytes)
715181Sgd78059 * has been stripped off, the packet data will be 4-byte aligned.
725181Sgd78059 * The remaining space can be used by upstream modules to prepend
735181Sgd78059 * any headers required.
745181Sgd78059 *
755181Sgd78059 * Patchable globals:
765181Sgd78059 *
775181Sgd78059 * dmfe_bus_modes: the bus mode bits to be put into CSR0.
785181Sgd78059 * Setting READ_MULTIPLE in this register seems to cause
795181Sgd78059 * the chip to generate a READ LINE command with a parity
805181Sgd78059 * error! Don't do it!
815181Sgd78059 *
825181Sgd78059 * dmfe_setup_desc1: the value to be put into descriptor word 1
835181Sgd78059 * when sending a SETUP packet.
845181Sgd78059 *
855181Sgd78059 * Setting TX_LAST_DESC in desc1 in a setup packet seems
865181Sgd78059 * to make the chip spontaneously reset internally - it
875181Sgd78059 * attempts to give back the setup packet descriptor by
885181Sgd78059 * writing to PCI address 00000000 - which may or may not
895181Sgd78059 * get a MASTER ABORT - after which most of its registers
905181Sgd78059 * seem to have either default values or garbage!
915181Sgd78059 *
925181Sgd78059 * TX_FIRST_DESC doesn't seem to have the same effect but
935181Sgd78059 * it isn't needed on a setup packet so we'll leave it out
945181Sgd78059 * too, just in case it has some other wierd side-effect.
955181Sgd78059 *
965181Sgd78059 * The default hardware packet filtering mode is now
975181Sgd78059 * HASH_AND_PERFECT (imperfect filtering of multicast
985181Sgd78059 * packets and perfect filtering of unicast packets).
995181Sgd78059 * If this is found not to work reliably, setting the
1005181Sgd78059 * TX_FILTER_TYPE1 bit will cause a switchover to using
1015181Sgd78059 * HASH_ONLY mode (imperfect filtering of *all* packets).
1025181Sgd78059 * Software will then perform the additional filtering
1035181Sgd78059 * as required.
1045181Sgd78059 */
1055181Sgd78059
1065181Sgd78059 #define DMFE_PCI_RNUMBER 2
1075181Sgd78059 #define DMFE_SLOP (8*sizeof (uint32_t))
1085181Sgd78059 #define DMFE_BUF_SIZE 2048
1095181Sgd78059 #define DMFE_BUF_SIZE_1 2000
1105181Sgd78059 #define DMFE_DMA_MODE DDI_DMA_STREAMING
1115181Sgd78059 #define DMFE_HEADROOM 34
1125181Sgd78059
1135181Sgd78059 static uint32_t dmfe_bus_modes = TX_POLL_INTVL | CACHE_ALIGN;
1145181Sgd78059 static uint32_t dmfe_setup_desc1 = TX_SETUP_PACKET | SETUPBUF_SIZE |
1155181Sgd78059 TX_FILTER_TYPE0;
1165181Sgd78059
1175181Sgd78059 /*
1185181Sgd78059 * Some tunable parameters ...
1195181Sgd78059 * Number of RX/TX ring entries (128/128)
1205181Sgd78059 * Minimum number of TX ring slots to keep free (1)
1215181Sgd78059 * Low-water mark at which to try to reclaim TX ring slots (1)
1225181Sgd78059 * How often to take a TX-done interrupt (twice per ring cycle)
1235181Sgd78059 * Whether to reclaim TX ring entries on a TX-done interrupt (no)
1245181Sgd78059 */
1255181Sgd78059
1265181Sgd78059 #define DMFE_TX_DESC 128 /* Should be a multiple of 4 <= 256 */
1275181Sgd78059 #define DMFE_RX_DESC 128 /* Should be a multiple of 4 <= 256 */
1285181Sgd78059
1295181Sgd78059 static uint32_t dmfe_rx_desc = DMFE_RX_DESC;
1305181Sgd78059 static uint32_t dmfe_tx_desc = DMFE_TX_DESC;
1315181Sgd78059 static uint32_t dmfe_tx_min_free = 1;
1325181Sgd78059 static uint32_t dmfe_tx_reclaim_level = 1;
1335181Sgd78059 static uint32_t dmfe_tx_int_factor = (DMFE_TX_DESC / 2) - 1;
1345181Sgd78059 static boolean_t dmfe_reclaim_on_done = B_FALSE;
1355181Sgd78059
1365181Sgd78059 /*
1375181Sgd78059 * Time-related parameters:
1385181Sgd78059 *
1395181Sgd78059 * We use a cyclic to provide a periodic callback; this is then used
1405181Sgd78059 * to check for TX-stall and poll the link status register.
1415181Sgd78059 *
1425181Sgd78059 * DMFE_TICK is the interval between cyclic callbacks, in microseconds.
1435181Sgd78059 *
1445181Sgd78059 * TX_STALL_TIME_100 is the timeout in microseconds between passing
1455181Sgd78059 * a packet to the chip for transmission and seeing that it's gone,
1465181Sgd78059 * when running at 100Mb/s. If we haven't reclaimed at least one
1475181Sgd78059 * descriptor in this time we assume the transmitter has stalled
1485181Sgd78059 * and reset the chip.
1495181Sgd78059 *
1505181Sgd78059 * TX_STALL_TIME_10 is the equivalent timeout when running at 10Mb/s.
1515181Sgd78059 *
1525181Sgd78059 * Patchable globals:
1535181Sgd78059 *
1545181Sgd78059 * dmfe_tick_us: DMFE_TICK
1555181Sgd78059 * dmfe_tx100_stall_us: TX_STALL_TIME_100
1565181Sgd78059 * dmfe_tx10_stall_us: TX_STALL_TIME_10
1575181Sgd78059 *
1585181Sgd78059 * These are then used in _init() to calculate:
1595181Sgd78059 *
1605181Sgd78059 * stall_100_tix[]: number of consecutive cyclic callbacks without a
1615181Sgd78059 * reclaim before the TX process is considered stalled,
1625181Sgd78059 * when running at 100Mb/s. The elements are indexed
1635181Sgd78059 * by transmit-engine-state.
1645181Sgd78059 * stall_10_tix[]: number of consecutive cyclic callbacks without a
1655181Sgd78059 * reclaim before the TX process is considered stalled,
1665181Sgd78059 * when running at 10Mb/s. The elements are indexed
1675181Sgd78059 * by transmit-engine-state.
1685181Sgd78059 */
1695181Sgd78059
1705181Sgd78059 #define DMFE_TICK 25000 /* microseconds */
1715181Sgd78059 #define TX_STALL_TIME_100 50000 /* microseconds */
1725181Sgd78059 #define TX_STALL_TIME_10 200000 /* microseconds */
1735181Sgd78059
1745181Sgd78059 static uint32_t dmfe_tick_us = DMFE_TICK;
1755181Sgd78059 static uint32_t dmfe_tx100_stall_us = TX_STALL_TIME_100;
1765181Sgd78059 static uint32_t dmfe_tx10_stall_us = TX_STALL_TIME_10;
1775181Sgd78059
1785181Sgd78059 /*
1795181Sgd78059 * Calculated from above in _init()
1805181Sgd78059 */
1815181Sgd78059
1825181Sgd78059 static uint32_t stall_100_tix[TX_PROCESS_MAX_STATE+1];
1835181Sgd78059 static uint32_t stall_10_tix[TX_PROCESS_MAX_STATE+1];
1845181Sgd78059
1855181Sgd78059 /*
1865181Sgd78059 * Property names
1875181Sgd78059 */
1885181Sgd78059 static char localmac_propname[] = "local-mac-address";
1895181Sgd78059 static char opmode_propname[] = "opmode-reg-value";
1905181Sgd78059
1915181Sgd78059 static int dmfe_m_start(void *);
1925181Sgd78059 static void dmfe_m_stop(void *);
1935181Sgd78059 static int dmfe_m_promisc(void *, boolean_t);
1945181Sgd78059 static int dmfe_m_multicst(void *, boolean_t, const uint8_t *);
1955181Sgd78059 static int dmfe_m_unicst(void *, const uint8_t *);
1965181Sgd78059 static void dmfe_m_ioctl(void *, queue_t *, mblk_t *);
1975181Sgd78059 static mblk_t *dmfe_m_tx(void *, mblk_t *);
1985181Sgd78059 static int dmfe_m_stat(void *, uint_t, uint64_t *);
1999860Sgdamore@opensolaris.org static int dmfe_m_getprop(void *, const char *, mac_prop_id_t,
200*11878SVenu.Iyer@Sun.COM uint_t, void *);
2019860Sgdamore@opensolaris.org static int dmfe_m_setprop(void *, const char *, mac_prop_id_t,
2029860Sgdamore@opensolaris.org uint_t, const void *);
203*11878SVenu.Iyer@Sun.COM static void dmfe_m_propinfo(void *, const char *, mac_prop_id_t,
204*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
2055181Sgd78059
2065181Sgd78059 static mac_callbacks_t dmfe_m_callbacks = {
207*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2085181Sgd78059 dmfe_m_stat,
2095181Sgd78059 dmfe_m_start,
2105181Sgd78059 dmfe_m_stop,
2115181Sgd78059 dmfe_m_promisc,
2125181Sgd78059 dmfe_m_multicst,
2135181Sgd78059 dmfe_m_unicst,
2145181Sgd78059 dmfe_m_tx,
215*11878SVenu.Iyer@Sun.COM NULL,
2168275SEric Cheng dmfe_m_ioctl,
2179860Sgdamore@opensolaris.org NULL, /* getcapab */
2189860Sgdamore@opensolaris.org NULL, /* open */
2199860Sgdamore@opensolaris.org NULL, /* close */
2209860Sgdamore@opensolaris.org dmfe_m_setprop,
221*11878SVenu.Iyer@Sun.COM dmfe_m_getprop,
222*11878SVenu.Iyer@Sun.COM dmfe_m_propinfo
2235181Sgd78059 };
2245181Sgd78059
2255181Sgd78059
2265181Sgd78059 /*
2275181Sgd78059 * Describes the chip's DMA engine
2285181Sgd78059 */
2295181Sgd78059 static ddi_dma_attr_t dma_attr = {
2305181Sgd78059 DMA_ATTR_V0, /* dma_attr version */
2315181Sgd78059 0, /* dma_attr_addr_lo */
2325181Sgd78059 (uint32_t)0xFFFFFFFF, /* dma_attr_addr_hi */
2335181Sgd78059 0x0FFFFFF, /* dma_attr_count_max */
2345181Sgd78059 0x20, /* dma_attr_align */
2355181Sgd78059 0x7F, /* dma_attr_burstsizes */
2365181Sgd78059 1, /* dma_attr_minxfer */
2375181Sgd78059 (uint32_t)0xFFFFFFFF, /* dma_attr_maxxfer */
2385181Sgd78059 (uint32_t)0xFFFFFFFF, /* dma_attr_seg */
2395181Sgd78059 1, /* dma_attr_sgllen */
2405181Sgd78059 1, /* dma_attr_granular */
2415181Sgd78059 0 /* dma_attr_flags */
2425181Sgd78059 };
2435181Sgd78059
2445181Sgd78059 /*
2455181Sgd78059 * DMA access attributes for registers and descriptors
2465181Sgd78059 */
2475181Sgd78059 static ddi_device_acc_attr_t dmfe_reg_accattr = {
2485181Sgd78059 DDI_DEVICE_ATTR_V0,
2495181Sgd78059 DDI_STRUCTURE_LE_ACC,
2505181Sgd78059 DDI_STRICTORDER_ACC
2515181Sgd78059 };
2525181Sgd78059
2535181Sgd78059 /*
2545181Sgd78059 * DMA access attributes for data: NOT to be byte swapped.
2555181Sgd78059 */
2565181Sgd78059 static ddi_device_acc_attr_t dmfe_data_accattr = {
2575181Sgd78059 DDI_DEVICE_ATTR_V0,
2585181Sgd78059 DDI_NEVERSWAP_ACC,
2595181Sgd78059 DDI_STRICTORDER_ACC
2605181Sgd78059 };
2615181Sgd78059
2625181Sgd78059 static uchar_t dmfe_broadcast_addr[ETHERADDRL] = {
2635181Sgd78059 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2645181Sgd78059 };
2655181Sgd78059
2665181Sgd78059
2675181Sgd78059 /*
2685181Sgd78059 * ========== Lowest-level chip register & ring access routines ==========
2695181Sgd78059 */
2705181Sgd78059
2715181Sgd78059 /*
2725181Sgd78059 * I/O register get/put routines
2735181Sgd78059 */
2745181Sgd78059 uint32_t
dmfe_chip_get32(dmfe_t * dmfep,off_t offset)2755181Sgd78059 dmfe_chip_get32(dmfe_t *dmfep, off_t offset)
2765181Sgd78059 {
2776990Sgd78059 uint32_t *addr;
2786990Sgd78059
2796990Sgd78059 addr = (void *)(dmfep->io_reg + offset);
2806990Sgd78059 return (ddi_get32(dmfep->io_handle, addr));
2815181Sgd78059 }
2825181Sgd78059
2835181Sgd78059 void
dmfe_chip_put32(dmfe_t * dmfep,off_t offset,uint32_t value)2845181Sgd78059 dmfe_chip_put32(dmfe_t *dmfep, off_t offset, uint32_t value)
2855181Sgd78059 {
2866990Sgd78059 uint32_t *addr;
2876990Sgd78059
2886990Sgd78059 addr = (void *)(dmfep->io_reg + offset);
2896990Sgd78059 ddi_put32(dmfep->io_handle, addr, value);
2905181Sgd78059 }
2915181Sgd78059
2925181Sgd78059 /*
2935181Sgd78059 * TX/RX ring get/put routines
2945181Sgd78059 */
2955181Sgd78059 static uint32_t
dmfe_ring_get32(dma_area_t * dma_p,uint_t index,uint_t offset)2965181Sgd78059 dmfe_ring_get32(dma_area_t *dma_p, uint_t index, uint_t offset)
2975181Sgd78059 {
2985181Sgd78059 uint32_t *addr;
2995181Sgd78059
3006990Sgd78059 addr = (void *)dma_p->mem_va;
3015181Sgd78059 return (ddi_get32(dma_p->acc_hdl, addr + index*DESC_SIZE + offset));
3025181Sgd78059 }
3035181Sgd78059
3045181Sgd78059 static void
dmfe_ring_put32(dma_area_t * dma_p,uint_t index,uint_t offset,uint32_t value)3055181Sgd78059 dmfe_ring_put32(dma_area_t *dma_p, uint_t index, uint_t offset, uint32_t value)
3065181Sgd78059 {
3075181Sgd78059 uint32_t *addr;
3085181Sgd78059
3096990Sgd78059 addr = (void *)dma_p->mem_va;
3105181Sgd78059 ddi_put32(dma_p->acc_hdl, addr + index*DESC_SIZE + offset, value);
3115181Sgd78059 }
3125181Sgd78059
3135181Sgd78059 /*
3145181Sgd78059 * Setup buffer get/put routines
3155181Sgd78059 */
3165181Sgd78059 static uint32_t
dmfe_setup_get32(dma_area_t * dma_p,uint_t index)3175181Sgd78059 dmfe_setup_get32(dma_area_t *dma_p, uint_t index)
3185181Sgd78059 {
3195181Sgd78059 uint32_t *addr;
3205181Sgd78059
3216990Sgd78059 addr = (void *)dma_p->setup_va;
3225181Sgd78059 return (ddi_get32(dma_p->acc_hdl, addr + index));
3235181Sgd78059 }
3245181Sgd78059
3255181Sgd78059 static void
dmfe_setup_put32(dma_area_t * dma_p,uint_t index,uint32_t value)3265181Sgd78059 dmfe_setup_put32(dma_area_t *dma_p, uint_t index, uint32_t value)
3275181Sgd78059 {
3285181Sgd78059 uint32_t *addr;
3295181Sgd78059
3306990Sgd78059 addr = (void *)dma_p->setup_va;
3315181Sgd78059 ddi_put32(dma_p->acc_hdl, addr + index, value);
3325181Sgd78059 }
3335181Sgd78059
3345181Sgd78059
3355181Sgd78059 /*
3365181Sgd78059 * ========== Low-level chip & ring buffer manipulation ==========
3375181Sgd78059 */
3385181Sgd78059
3395181Sgd78059 /*
3405181Sgd78059 * dmfe_set_opmode() -- function to set operating mode
3415181Sgd78059 */
3425181Sgd78059 static void
dmfe_set_opmode(dmfe_t * dmfep)3435181Sgd78059 dmfe_set_opmode(dmfe_t *dmfep)
3445181Sgd78059 {
3455181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
3465181Sgd78059
3475181Sgd78059 dmfe_chip_put32(dmfep, OPN_MODE_REG, dmfep->opmode);
3485181Sgd78059 drv_usecwait(10);
3495181Sgd78059 }
3505181Sgd78059
3515181Sgd78059 /*
3525181Sgd78059 * dmfe_stop_chip() -- stop all chip processing & optionally reset the h/w
3535181Sgd78059 */
3545181Sgd78059 static void
dmfe_stop_chip(dmfe_t * dmfep,enum chip_state newstate)3555181Sgd78059 dmfe_stop_chip(dmfe_t *dmfep, enum chip_state newstate)
3565181Sgd78059 {
3575181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
3585181Sgd78059
3595181Sgd78059 /*
3605181Sgd78059 * Stop the chip:
3615181Sgd78059 * disable all interrupts
3625181Sgd78059 * stop TX/RX processes
3635181Sgd78059 * clear the status bits for TX/RX stopped
3645181Sgd78059 * If required, reset the chip
3655181Sgd78059 * Record the new state
3665181Sgd78059 */
3675181Sgd78059 dmfe_chip_put32(dmfep, INT_MASK_REG, 0);
3685181Sgd78059 dmfep->opmode &= ~(START_TRANSMIT | START_RECEIVE);
3695181Sgd78059 dmfe_set_opmode(dmfep);
3705181Sgd78059 dmfe_chip_put32(dmfep, STATUS_REG, TX_STOPPED_INT | RX_STOPPED_INT);
3715181Sgd78059
3725181Sgd78059 switch (newstate) {
3735181Sgd78059 default:
3745181Sgd78059 ASSERT(!"can't get here");
3755181Sgd78059 return;
3765181Sgd78059
3775181Sgd78059 case CHIP_STOPPED:
3785181Sgd78059 case CHIP_ERROR:
3795181Sgd78059 break;
3805181Sgd78059
3815181Sgd78059 case CHIP_RESET:
3825181Sgd78059 dmfe_chip_put32(dmfep, BUS_MODE_REG, SW_RESET);
3835181Sgd78059 drv_usecwait(10);
3845181Sgd78059 dmfe_chip_put32(dmfep, BUS_MODE_REG, 0);
3855181Sgd78059 drv_usecwait(10);
3865181Sgd78059 dmfe_chip_put32(dmfep, BUS_MODE_REG, dmfe_bus_modes);
3875181Sgd78059 break;
3885181Sgd78059 }
3895181Sgd78059
3905181Sgd78059 dmfep->chip_state = newstate;
3915181Sgd78059 }
3925181Sgd78059
3935181Sgd78059 /*
3945181Sgd78059 * Initialize transmit and receive descriptor rings, and
3955181Sgd78059 * set the chip to point to the first entry in each ring
3965181Sgd78059 */
3975181Sgd78059 static void
dmfe_init_rings(dmfe_t * dmfep)3985181Sgd78059 dmfe_init_rings(dmfe_t *dmfep)
3995181Sgd78059 {
4005181Sgd78059 dma_area_t *descp;
4015181Sgd78059 uint32_t pstart;
4025181Sgd78059 uint32_t pnext;
4035181Sgd78059 uint32_t pbuff;
4045181Sgd78059 uint32_t desc1;
4055181Sgd78059 int i;
4065181Sgd78059
4075181Sgd78059 /*
4085181Sgd78059 * You need all the locks in order to rewrite the descriptor rings
4095181Sgd78059 */
4105181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
4115181Sgd78059 ASSERT(mutex_owned(dmfep->rxlock));
4125181Sgd78059 ASSERT(mutex_owned(dmfep->txlock));
4135181Sgd78059
4145181Sgd78059 /*
4155181Sgd78059 * Program the RX ring entries
4165181Sgd78059 */
4175181Sgd78059 descp = &dmfep->rx_desc;
4185181Sgd78059 pstart = descp->mem_dvma;
4195181Sgd78059 pnext = pstart + sizeof (struct rx_desc_type);
4205181Sgd78059 pbuff = dmfep->rx_buff.mem_dvma;
4215181Sgd78059 desc1 = RX_CHAINING | DMFE_BUF_SIZE_1;
4225181Sgd78059
4235181Sgd78059 for (i = 0; i < dmfep->rx.n_desc; ++i) {
4245181Sgd78059 dmfe_ring_put32(descp, i, RD_NEXT, pnext);
4255181Sgd78059 dmfe_ring_put32(descp, i, BUFFER1, pbuff);
4265181Sgd78059 dmfe_ring_put32(descp, i, DESC1, desc1);
4275181Sgd78059 dmfe_ring_put32(descp, i, DESC0, RX_OWN);
4285181Sgd78059
4295181Sgd78059 pnext += sizeof (struct rx_desc_type);
4305181Sgd78059 pbuff += DMFE_BUF_SIZE;
4315181Sgd78059 }
4325181Sgd78059
4335181Sgd78059 /*
4345181Sgd78059 * Fix up last entry & sync
4355181Sgd78059 */
4365181Sgd78059 dmfe_ring_put32(descp, --i, RD_NEXT, pstart);
4375181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
4385181Sgd78059 dmfep->rx.next_free = 0;
4395181Sgd78059
4405181Sgd78059 /*
4415181Sgd78059 * Set the base address of the RX descriptor list in CSR3
4425181Sgd78059 */
4435181Sgd78059 dmfe_chip_put32(dmfep, RX_BASE_ADDR_REG, descp->mem_dvma);
4445181Sgd78059
4455181Sgd78059 /*
4465181Sgd78059 * Program the TX ring entries
4475181Sgd78059 */
4485181Sgd78059 descp = &dmfep->tx_desc;
4495181Sgd78059 pstart = descp->mem_dvma;
4505181Sgd78059 pnext = pstart + sizeof (struct tx_desc_type);
4515181Sgd78059 pbuff = dmfep->tx_buff.mem_dvma;
4525181Sgd78059 desc1 = TX_CHAINING;
4535181Sgd78059
4545181Sgd78059 for (i = 0; i < dmfep->tx.n_desc; ++i) {
4555181Sgd78059 dmfe_ring_put32(descp, i, TD_NEXT, pnext);
4565181Sgd78059 dmfe_ring_put32(descp, i, BUFFER1, pbuff);
4575181Sgd78059 dmfe_ring_put32(descp, i, DESC1, desc1);
4585181Sgd78059 dmfe_ring_put32(descp, i, DESC0, 0);
4595181Sgd78059
4605181Sgd78059 pnext += sizeof (struct tx_desc_type);
4615181Sgd78059 pbuff += DMFE_BUF_SIZE;
4625181Sgd78059 }
4635181Sgd78059
4645181Sgd78059 /*
4655181Sgd78059 * Fix up last entry & sync
4665181Sgd78059 */
4675181Sgd78059 dmfe_ring_put32(descp, --i, TD_NEXT, pstart);
4685181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
4695181Sgd78059 dmfep->tx.n_free = dmfep->tx.n_desc;
4705181Sgd78059 dmfep->tx.next_free = dmfep->tx.next_busy = 0;
4715181Sgd78059
4725181Sgd78059 /*
4735181Sgd78059 * Set the base address of the TX descrptor list in CSR4
4745181Sgd78059 */
4755181Sgd78059 dmfe_chip_put32(dmfep, TX_BASE_ADDR_REG, descp->mem_dvma);
4765181Sgd78059 }
4775181Sgd78059
4785181Sgd78059 /*
4795181Sgd78059 * dmfe_start_chip() -- start the chip transmitting and/or receiving
4805181Sgd78059 */
4815181Sgd78059 static void
dmfe_start_chip(dmfe_t * dmfep,int mode)4825181Sgd78059 dmfe_start_chip(dmfe_t *dmfep, int mode)
4835181Sgd78059 {
4845181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
4855181Sgd78059
4865181Sgd78059 dmfep->opmode |= mode;
4875181Sgd78059 dmfe_set_opmode(dmfep);
4885181Sgd78059
4895181Sgd78059 dmfe_chip_put32(dmfep, W_J_TIMER_REG, 0);
4905181Sgd78059 /*
4915181Sgd78059 * Enable VLAN length mode (allows packets to be 4 bytes Longer).
4925181Sgd78059 */
4935181Sgd78059 dmfe_chip_put32(dmfep, W_J_TIMER_REG, VLAN_ENABLE);
4945181Sgd78059
4955181Sgd78059 /*
4965181Sgd78059 * Clear any pending process-stopped interrupts
4975181Sgd78059 */
4985181Sgd78059 dmfe_chip_put32(dmfep, STATUS_REG, TX_STOPPED_INT | RX_STOPPED_INT);
4995181Sgd78059 dmfep->chip_state = mode & START_RECEIVE ? CHIP_TX_RX :
5005181Sgd78059 mode & START_TRANSMIT ? CHIP_TX_ONLY : CHIP_STOPPED;
5015181Sgd78059 }
5025181Sgd78059
5035181Sgd78059 /*
5045181Sgd78059 * dmfe_enable_interrupts() -- enable our favourite set of interrupts.
5055181Sgd78059 *
5065181Sgd78059 * Normal interrupts:
5075181Sgd78059 * We always enable:
5085181Sgd78059 * RX_PKTDONE_INT (packet received)
5095181Sgd78059 * TX_PKTDONE_INT (TX complete)
5105181Sgd78059 * We never enable:
5115181Sgd78059 * TX_ALLDONE_INT (next TX buffer not ready)
5125181Sgd78059 *
5135181Sgd78059 * Abnormal interrupts:
5145181Sgd78059 * We always enable:
5155181Sgd78059 * RX_STOPPED_INT
5165181Sgd78059 * TX_STOPPED_INT
5175181Sgd78059 * SYSTEM_ERR_INT
5185181Sgd78059 * RX_UNAVAIL_INT
5195181Sgd78059 * We never enable:
5205181Sgd78059 * RX_EARLY_INT
5215181Sgd78059 * RX_WATCHDOG_INT
5225181Sgd78059 * TX_JABBER_INT
5235181Sgd78059 * TX_EARLY_INT
5245181Sgd78059 * TX_UNDERFLOW_INT
5255181Sgd78059 * GP_TIMER_INT (not valid in -9 chips)
5265181Sgd78059 * LINK_STATUS_INT (not valid in -9 chips)
5275181Sgd78059 */
5285181Sgd78059 static void
dmfe_enable_interrupts(dmfe_t * dmfep)5295181Sgd78059 dmfe_enable_interrupts(dmfe_t *dmfep)
5305181Sgd78059 {
5315181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
5325181Sgd78059
5335181Sgd78059 /*
5345181Sgd78059 * Put 'the standard set of interrupts' in the interrupt mask register
5355181Sgd78059 */
5365181Sgd78059 dmfep->imask = RX_PKTDONE_INT | TX_PKTDONE_INT |
5375181Sgd78059 RX_STOPPED_INT | TX_STOPPED_INT | RX_UNAVAIL_INT | SYSTEM_ERR_INT;
5385181Sgd78059
5395181Sgd78059 dmfe_chip_put32(dmfep, INT_MASK_REG,
5405181Sgd78059 NORMAL_SUMMARY_INT | ABNORMAL_SUMMARY_INT | dmfep->imask);
5415181Sgd78059 dmfep->chip_state = CHIP_RUNNING;
5425181Sgd78059 }
5435181Sgd78059
5445181Sgd78059 /*
5455181Sgd78059 * ========== RX side routines ==========
5465181Sgd78059 */
5475181Sgd78059
5485181Sgd78059 /*
5495181Sgd78059 * Function to update receive statistics on various errors
5505181Sgd78059 */
5515181Sgd78059 static void
dmfe_update_rx_stats(dmfe_t * dmfep,uint32_t desc0)5525181Sgd78059 dmfe_update_rx_stats(dmfe_t *dmfep, uint32_t desc0)
5535181Sgd78059 {
5545181Sgd78059 ASSERT(mutex_owned(dmfep->rxlock));
5555181Sgd78059
5565181Sgd78059 /*
5575181Sgd78059 * The error summary bit and the error bits that it summarises
5585181Sgd78059 * are only valid if this is the last fragment. Therefore, a
5595181Sgd78059 * fragment only contributes to the error statistics if both
5605181Sgd78059 * the last-fragment and error summary bits are set.
5615181Sgd78059 */
5625181Sgd78059 if (((RX_LAST_DESC | RX_ERR_SUMMARY) & ~desc0) == 0) {
5635181Sgd78059 dmfep->rx_stats_ierrors += 1;
5645181Sgd78059
5655181Sgd78059 /*
5665181Sgd78059 * There are some other error bits in the descriptor for
5675181Sgd78059 * which there don't seem to be appropriate MAC statistics,
5685181Sgd78059 * notably RX_COLLISION and perhaps RX_DESC_ERR. The
5695181Sgd78059 * latter may not be possible if it is supposed to indicate
5705181Sgd78059 * that one buffer has been filled with a partial packet
5715181Sgd78059 * and the next buffer required for the rest of the packet
5725181Sgd78059 * was not available, as all our buffers are more than large
5735181Sgd78059 * enough for a whole packet without fragmenting.
5745181Sgd78059 */
5755181Sgd78059
5765181Sgd78059 if (desc0 & RX_OVERFLOW) {
5775181Sgd78059 dmfep->rx_stats_overflow += 1;
5785181Sgd78059
5795181Sgd78059 } else if (desc0 & RX_RUNT_FRAME)
5805181Sgd78059 dmfep->rx_stats_short += 1;
5815181Sgd78059
5825181Sgd78059 if (desc0 & RX_CRC)
5835181Sgd78059 dmfep->rx_stats_fcs += 1;
5845181Sgd78059
5855181Sgd78059 if (desc0 & RX_FRAME2LONG)
5865181Sgd78059 dmfep->rx_stats_toolong += 1;
5875181Sgd78059 }
5885181Sgd78059
5895181Sgd78059 /*
5905181Sgd78059 * A receive watchdog timeout is counted as a MAC-level receive
5915181Sgd78059 * error. Strangely, it doesn't set the packet error summary bit,
5925181Sgd78059 * according to the chip data sheet :-?
5935181Sgd78059 */
5945181Sgd78059 if (desc0 & RX_RCV_WD_TO)
5955181Sgd78059 dmfep->rx_stats_macrcv_errors += 1;
5965181Sgd78059
5975181Sgd78059 if (desc0 & RX_DRIBBLING)
5985181Sgd78059 dmfep->rx_stats_align += 1;
5995181Sgd78059
6005181Sgd78059 if (desc0 & RX_MII_ERR)
6015181Sgd78059 dmfep->rx_stats_macrcv_errors += 1;
6025181Sgd78059 }
6035181Sgd78059
6045181Sgd78059 /*
6055181Sgd78059 * Receive incoming packet(s) and pass them up ...
6065181Sgd78059 */
6075181Sgd78059 static mblk_t *
dmfe_getp(dmfe_t * dmfep)6085181Sgd78059 dmfe_getp(dmfe_t *dmfep)
6095181Sgd78059 {
6105181Sgd78059 dma_area_t *descp;
6115181Sgd78059 mblk_t **tail;
6125181Sgd78059 mblk_t *head;
6135181Sgd78059 mblk_t *mp;
6145181Sgd78059 char *rxb;
6155181Sgd78059 uchar_t *dp;
6165181Sgd78059 uint32_t desc0;
6175181Sgd78059 uint32_t misses;
6185181Sgd78059 int packet_length;
6195181Sgd78059 int index;
6205181Sgd78059
6215181Sgd78059 mutex_enter(dmfep->rxlock);
6225181Sgd78059
6235181Sgd78059 /*
6245181Sgd78059 * Update the missed frame statistic from the on-chip counter.
6255181Sgd78059 */
6265181Sgd78059 misses = dmfe_chip_get32(dmfep, MISSED_FRAME_REG);
6275181Sgd78059 dmfep->rx_stats_norcvbuf += (misses & MISSED_FRAME_MASK);
6285181Sgd78059
6295181Sgd78059 /*
6305181Sgd78059 * sync (all) receive descriptors before inspecting them
6315181Sgd78059 */
6325181Sgd78059 descp = &dmfep->rx_desc;
6335181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORKERNEL);
6345181Sgd78059
6355181Sgd78059 /*
6365181Sgd78059 * We should own at least one RX entry, since we've had a
6375181Sgd78059 * receive interrupt, but let's not be dogmatic about it.
6385181Sgd78059 */
6395181Sgd78059 index = dmfep->rx.next_free;
6405181Sgd78059 desc0 = dmfe_ring_get32(descp, index, DESC0);
6419860Sgdamore@opensolaris.org
6429860Sgdamore@opensolaris.org DTRACE_PROBE1(rx__start, uint32_t, desc0);
6435181Sgd78059 for (head = NULL, tail = &head; (desc0 & RX_OWN) == 0; ) {
6445181Sgd78059 /*
6455181Sgd78059 * Maintain statistics for every descriptor returned
6465181Sgd78059 * to us by the chip ...
6475181Sgd78059 */
6485181Sgd78059 dmfe_update_rx_stats(dmfep, desc0);
6495181Sgd78059
6505181Sgd78059 /*
6515181Sgd78059 * Check that the entry has both "packet start" and
6525181Sgd78059 * "packet end" flags. We really shouldn't get packet
6535181Sgd78059 * fragments, 'cos all the RX buffers are bigger than
6545181Sgd78059 * the largest valid packet. So we'll just drop any
6555181Sgd78059 * fragments we find & skip on to the next entry.
6565181Sgd78059 */
6575181Sgd78059 if (((RX_FIRST_DESC | RX_LAST_DESC) & ~desc0) != 0) {
6589860Sgdamore@opensolaris.org DTRACE_PROBE1(rx__frag, uint32_t, desc0);
6595181Sgd78059 goto skip;
6605181Sgd78059 }
6615181Sgd78059
6625181Sgd78059 /*
6635181Sgd78059 * A whole packet in one buffer. We have to check error
6645181Sgd78059 * status and packet length before forwarding it upstream.
6655181Sgd78059 */
6665181Sgd78059 if (desc0 & RX_ERR_SUMMARY) {
6679860Sgdamore@opensolaris.org DTRACE_PROBE1(rx__err, uint32_t, desc0);
6685181Sgd78059 goto skip;
6695181Sgd78059 }
6705181Sgd78059
6715181Sgd78059 packet_length = (desc0 >> 16) & 0x3fff;
6725181Sgd78059 if (packet_length > DMFE_MAX_PKT_SIZE) {
6739860Sgdamore@opensolaris.org DTRACE_PROBE1(rx__toobig, int, packet_length);
6745181Sgd78059 goto skip;
6755181Sgd78059 } else if (packet_length < ETHERMIN) {
6765181Sgd78059 /*
6775181Sgd78059 * Note that VLAN packet would be even larger,
6785181Sgd78059 * but we don't worry about dropping runt VLAN
6795181Sgd78059 * frames.
6805181Sgd78059 *
6815181Sgd78059 * This check is probably redundant, as well,
6825181Sgd78059 * since the hardware should drop RUNT frames.
6835181Sgd78059 */
6849860Sgdamore@opensolaris.org DTRACE_PROBE1(rx__runt, int, packet_length);
6855181Sgd78059 goto skip;
6865181Sgd78059 }
6875181Sgd78059
6885181Sgd78059 /*
6895181Sgd78059 * Sync the data, so we can examine it; then check that
6905181Sgd78059 * the packet is really intended for us (remember that
6915181Sgd78059 * if we're using Imperfect Filtering, then the chip will
6925181Sgd78059 * receive unicast packets sent to stations whose addresses
6935181Sgd78059 * just happen to hash to the same value as our own; we
6945181Sgd78059 * discard these here so they don't get sent upstream ...)
6955181Sgd78059 */
6965181Sgd78059 (void) ddi_dma_sync(dmfep->rx_buff.dma_hdl,
6975181Sgd78059 index * DMFE_BUF_SIZE, DMFE_BUF_SIZE,
6985181Sgd78059 DDI_DMA_SYNC_FORKERNEL);
6995181Sgd78059 rxb = &dmfep->rx_buff.mem_va[index*DMFE_BUF_SIZE];
7005181Sgd78059
7015181Sgd78059
7025181Sgd78059 /*
7035181Sgd78059 * We do not bother to check that the packet is really for
7045181Sgd78059 * us, we let the MAC framework make that check instead.
7055181Sgd78059 * This is especially important if we ever want to support
7065181Sgd78059 * multiple MAC addresses.
7075181Sgd78059 */
7085181Sgd78059
7095181Sgd78059 /*
7105181Sgd78059 * Packet looks good; get a buffer to copy it into. We
7115181Sgd78059 * allow some space at the front of the allocated buffer
7125181Sgd78059 * (HEADROOM) in case any upstream modules want to prepend
7135181Sgd78059 * some sort of header. The value has been carefully chosen
7145181Sgd78059 * So that it also has the side-effect of making the packet
7155181Sgd78059 * *contents* 4-byte aligned, as required by NCA!
7165181Sgd78059 */
7175181Sgd78059 mp = allocb(DMFE_HEADROOM + packet_length, 0);
7185181Sgd78059 if (mp == NULL) {
7199860Sgdamore@opensolaris.org DTRACE_PROBE(rx__no__buf);
7205181Sgd78059 dmfep->rx_stats_norcvbuf += 1;
7215181Sgd78059 goto skip;
7225181Sgd78059 }
7235181Sgd78059
7245181Sgd78059 /*
7255181Sgd78059 * Account for statistics of good packets.
7265181Sgd78059 */
7275181Sgd78059 dmfep->rx_stats_ipackets += 1;
7285181Sgd78059 dmfep->rx_stats_rbytes += packet_length;
7295181Sgd78059 if (desc0 & RX_MULTI_FRAME) {
7305181Sgd78059 if (bcmp(rxb, dmfe_broadcast_addr, ETHERADDRL)) {
7315181Sgd78059 dmfep->rx_stats_multi += 1;
7325181Sgd78059 } else {
7335181Sgd78059 dmfep->rx_stats_bcast += 1;
7345181Sgd78059 }
7355181Sgd78059 }
7365181Sgd78059
7375181Sgd78059 /*
7385181Sgd78059 * Copy the packet into the STREAMS buffer
7395181Sgd78059 */
7405181Sgd78059 dp = mp->b_rptr += DMFE_HEADROOM;
7415181Sgd78059 mp->b_cont = mp->b_next = NULL;
7425181Sgd78059
7435181Sgd78059 /*
7445181Sgd78059 * Don't worry about stripping the vlan tag, the MAC
7455181Sgd78059 * layer will take care of that for us.
7465181Sgd78059 */
7475181Sgd78059 bcopy(rxb, dp, packet_length);
7485181Sgd78059
7495181Sgd78059 /*
7505181Sgd78059 * Fix up the packet length, and link it to the chain
7515181Sgd78059 */
7525181Sgd78059 mp->b_wptr = mp->b_rptr + packet_length - ETHERFCSL;
7535181Sgd78059 *tail = mp;
7545181Sgd78059 tail = &mp->b_next;
7555181Sgd78059
7565181Sgd78059 skip:
7575181Sgd78059 /*
7585181Sgd78059 * Return ownership of ring entry & advance to next
7595181Sgd78059 */
7605181Sgd78059 dmfe_ring_put32(descp, index, DESC0, RX_OWN);
7615181Sgd78059 index = NEXT(index, dmfep->rx.n_desc);
7625181Sgd78059 desc0 = dmfe_ring_get32(descp, index, DESC0);
7635181Sgd78059 }
7645181Sgd78059
7655181Sgd78059 /*
7665181Sgd78059 * Remember where to start looking next time ...
7675181Sgd78059 */
7685181Sgd78059 dmfep->rx.next_free = index;
7695181Sgd78059
7705181Sgd78059 /*
7715181Sgd78059 * sync the receive descriptors that we've given back
7725181Sgd78059 * (actually, we sync all of them for simplicity), and
7735181Sgd78059 * wake the chip in case it had suspended receive
7745181Sgd78059 */
7755181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
7765181Sgd78059 dmfe_chip_put32(dmfep, RX_POLL_REG, 0);
7775181Sgd78059
7785181Sgd78059 mutex_exit(dmfep->rxlock);
7795181Sgd78059 return (head);
7805181Sgd78059 }
7815181Sgd78059
7825181Sgd78059 /*
7835181Sgd78059 * ========== Primary TX side routines ==========
7845181Sgd78059 */
7855181Sgd78059
7865181Sgd78059 /*
7875181Sgd78059 * TX ring management:
7885181Sgd78059 *
7895181Sgd78059 * There are <tx.n_desc> entries in the ring, of which those from
7905181Sgd78059 * <tx.next_free> round to but not including <tx.next_busy> must
7915181Sgd78059 * be owned by the CPU. The number of such entries should equal
7925181Sgd78059 * <tx.n_free>; but there may also be some more entries which the
7935181Sgd78059 * chip has given back but which we haven't yet accounted for.
7945181Sgd78059 * The routine dmfe_reclaim_tx_desc() adjusts the indexes & counts
7955181Sgd78059 * as it discovers such entries.
7965181Sgd78059 *
7975181Sgd78059 * Initially, or when the ring is entirely free:
7985181Sgd78059 * C = Owned by CPU
7995181Sgd78059 * D = Owned by Davicom (DMFE) chip
8005181Sgd78059 *
8015181Sgd78059 * tx.next_free tx.n_desc = 16
8025181Sgd78059 * |
8035181Sgd78059 * v
8045181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8055181Sgd78059 * | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C |
8065181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8075181Sgd78059 * ^
8085181Sgd78059 * |
8095181Sgd78059 * tx.next_busy tx.n_free = 16
8105181Sgd78059 *
8115181Sgd78059 * On entry to reclaim() during normal use:
8125181Sgd78059 *
8135181Sgd78059 * tx.next_free tx.n_desc = 16
8145181Sgd78059 * |
8155181Sgd78059 * v
8165181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8175181Sgd78059 * | C | C | C | C | C | C | D | D | D | C | C | C | C | C | C | C |
8185181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8195181Sgd78059 * ^
8205181Sgd78059 * |
8215181Sgd78059 * tx.next_busy tx.n_free = 9
8225181Sgd78059 *
8235181Sgd78059 * On exit from reclaim():
8245181Sgd78059 *
8255181Sgd78059 * tx.next_free tx.n_desc = 16
8265181Sgd78059 * |
8275181Sgd78059 * v
8285181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8295181Sgd78059 * | C | C | C | C | C | C | D | D | D | C | C | C | C | C | C | C |
8305181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8315181Sgd78059 * ^
8325181Sgd78059 * |
8335181Sgd78059 * tx.next_busy tx.n_free = 13
8345181Sgd78059 *
8355181Sgd78059 * The ring is considered "full" when only one entry is owned by
8365181Sgd78059 * the CPU; thus <tx.n_free> should always be >= 1.
8375181Sgd78059 *
8385181Sgd78059 * tx.next_free tx.n_desc = 16
8395181Sgd78059 * |
8405181Sgd78059 * v
8415181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8425181Sgd78059 * | D | D | D | D | D | C | D | D | D | D | D | D | D | D | D | D |
8435181Sgd78059 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8445181Sgd78059 * ^
8455181Sgd78059 * |
8465181Sgd78059 * tx.next_busy tx.n_free = 1
8475181Sgd78059 */
8485181Sgd78059
8495181Sgd78059 /*
8505181Sgd78059 * Function to update transmit statistics on various errors
8515181Sgd78059 */
8525181Sgd78059 static void
dmfe_update_tx_stats(dmfe_t * dmfep,int index,uint32_t desc0,uint32_t desc1)8535181Sgd78059 dmfe_update_tx_stats(dmfe_t *dmfep, int index, uint32_t desc0, uint32_t desc1)
8545181Sgd78059 {
8555181Sgd78059 uint32_t collisions;
8565181Sgd78059 uint32_t errbits;
8575181Sgd78059 uint32_t errsum;
8585181Sgd78059
8595181Sgd78059 ASSERT(mutex_owned(dmfep->txlock));
8605181Sgd78059
8615181Sgd78059 collisions = ((desc0 >> 3) & 0x0f);
8625181Sgd78059 errsum = desc0 & TX_ERR_SUMMARY;
8635181Sgd78059 errbits = desc0 & (TX_UNDERFLOW | TX_LATE_COLL | TX_CARRIER_LOSS |
8645181Sgd78059 TX_NO_CARRIER | TX_EXCESS_COLL | TX_JABBER_TO);
8655181Sgd78059 if ((errsum == 0) != (errbits == 0)) {
8665181Sgd78059 dmfe_log(dmfep, "dubious TX error status 0x%x", desc0);
8675181Sgd78059 desc0 |= TX_ERR_SUMMARY;
8685181Sgd78059 }
8695181Sgd78059
8705181Sgd78059 if (desc0 & TX_ERR_SUMMARY) {
8715181Sgd78059 dmfep->tx_stats_oerrors += 1;
8725181Sgd78059
8735181Sgd78059 /*
8745181Sgd78059 * If we ever see a transmit jabber timeout, we count it
8755181Sgd78059 * as a MAC-level transmit error; but we probably won't
8765181Sgd78059 * see it as it causes an Abnormal interrupt and we reset
8775181Sgd78059 * the chip in order to recover
8785181Sgd78059 */
8795181Sgd78059 if (desc0 & TX_JABBER_TO) {
8805181Sgd78059 dmfep->tx_stats_macxmt_errors += 1;
8815181Sgd78059 dmfep->tx_stats_jabber += 1;
8825181Sgd78059 }
8835181Sgd78059
8845181Sgd78059 if (desc0 & TX_UNDERFLOW)
8855181Sgd78059 dmfep->tx_stats_underflow += 1;
8865181Sgd78059 else if (desc0 & TX_LATE_COLL)
8875181Sgd78059 dmfep->tx_stats_xmtlatecoll += 1;
8885181Sgd78059
8895181Sgd78059 if (desc0 & (TX_CARRIER_LOSS | TX_NO_CARRIER))
8905181Sgd78059 dmfep->tx_stats_nocarrier += 1;
8915181Sgd78059
8925181Sgd78059 if (desc0 & TX_EXCESS_COLL) {
8935181Sgd78059 dmfep->tx_stats_excoll += 1;
8945181Sgd78059 collisions = 16;
8955181Sgd78059 }
8965181Sgd78059 } else {
8975181Sgd78059 int bit = index % NBBY;
8985181Sgd78059 int byt = index / NBBY;
8995181Sgd78059
9005181Sgd78059 if (dmfep->tx_mcast[byt] & bit) {
9015181Sgd78059 dmfep->tx_mcast[byt] &= ~bit;
9025181Sgd78059 dmfep->tx_stats_multi += 1;
9035181Sgd78059
9045181Sgd78059 } else if (dmfep->tx_bcast[byt] & bit) {
9055181Sgd78059 dmfep->tx_bcast[byt] &= ~bit;
9065181Sgd78059 dmfep->tx_stats_bcast += 1;
9075181Sgd78059 }
9085181Sgd78059
9095181Sgd78059 dmfep->tx_stats_opackets += 1;
9105181Sgd78059 dmfep->tx_stats_obytes += desc1 & TX_BUFFER_SIZE1;
9115181Sgd78059 }
9125181Sgd78059
9135181Sgd78059 if (collisions == 1)
9145181Sgd78059 dmfep->tx_stats_first_coll += 1;
9155181Sgd78059 else if (collisions != 0)
9165181Sgd78059 dmfep->tx_stats_multi_coll += 1;
9175181Sgd78059 dmfep->tx_stats_collisions += collisions;
9185181Sgd78059
9195181Sgd78059 if (desc0 & TX_DEFERRED)
9205181Sgd78059 dmfep->tx_stats_defer += 1;
9215181Sgd78059 }
9225181Sgd78059
9235181Sgd78059 /*
9245181Sgd78059 * Reclaim all the ring entries that the chip has returned to us ...
9255181Sgd78059 *
9265181Sgd78059 * Returns B_FALSE if no entries could be reclaimed. Otherwise, reclaims
9275181Sgd78059 * as many as possible, restarts the TX stall timeout, and returns B_TRUE.
9285181Sgd78059 */
9295181Sgd78059 static boolean_t
dmfe_reclaim_tx_desc(dmfe_t * dmfep)9305181Sgd78059 dmfe_reclaim_tx_desc(dmfe_t *dmfep)
9315181Sgd78059 {
9325181Sgd78059 dma_area_t *descp;
9335181Sgd78059 uint32_t desc0;
9345181Sgd78059 uint32_t desc1;
9355181Sgd78059 int i;
9365181Sgd78059
9375181Sgd78059 ASSERT(mutex_owned(dmfep->txlock));
9385181Sgd78059
9395181Sgd78059 /*
9405181Sgd78059 * sync transmit descriptor ring before looking at it
9415181Sgd78059 */
9425181Sgd78059 descp = &dmfep->tx_desc;
9435181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORKERNEL);
9445181Sgd78059
9455181Sgd78059 /*
9465181Sgd78059 * Early exit if there are no descriptors to reclaim, either
9475181Sgd78059 * because they're all reclaimed already, or because the next
9485181Sgd78059 * one is still owned by the chip ...
9495181Sgd78059 */
9505181Sgd78059 i = dmfep->tx.next_busy;
9515181Sgd78059 if (i == dmfep->tx.next_free)
9525181Sgd78059 return (B_FALSE);
9535181Sgd78059 desc0 = dmfe_ring_get32(descp, i, DESC0);
9545181Sgd78059 if (desc0 & TX_OWN)
9555181Sgd78059 return (B_FALSE);
9565181Sgd78059
9575181Sgd78059 /*
9585181Sgd78059 * Reclaim as many descriptors as possible ...
9595181Sgd78059 */
9605181Sgd78059 for (;;) {
9615181Sgd78059 desc1 = dmfe_ring_get32(descp, i, DESC1);
9625181Sgd78059 ASSERT((desc1 & (TX_SETUP_PACKET | TX_LAST_DESC)) != 0);
9635181Sgd78059
9649860Sgdamore@opensolaris.org if ((desc1 & TX_SETUP_PACKET) == 0) {
9655181Sgd78059 /*
9665181Sgd78059 * Regular packet - just update stats
9675181Sgd78059 */
9685181Sgd78059 dmfe_update_tx_stats(dmfep, i, desc0, desc1);
9695181Sgd78059 }
9705181Sgd78059
9715181Sgd78059 /*
9725181Sgd78059 * Update count & index; we're all done if the ring is
9735181Sgd78059 * now fully reclaimed, or the next entry if still owned
9745181Sgd78059 * by the chip ...
9755181Sgd78059 */
9765181Sgd78059 dmfep->tx.n_free += 1;
9775181Sgd78059 i = NEXT(i, dmfep->tx.n_desc);
9785181Sgd78059 if (i == dmfep->tx.next_free)
9795181Sgd78059 break;
9805181Sgd78059 desc0 = dmfe_ring_get32(descp, i, DESC0);
9815181Sgd78059 if (desc0 & TX_OWN)
9825181Sgd78059 break;
9835181Sgd78059 }
9845181Sgd78059
9855181Sgd78059 dmfep->tx.next_busy = i;
9865181Sgd78059 dmfep->tx_pending_tix = 0;
9875181Sgd78059 return (B_TRUE);
9885181Sgd78059 }
9895181Sgd78059
9905181Sgd78059 /*
9915181Sgd78059 * Send the message in the message block chain <mp>.
9925181Sgd78059 *
9935181Sgd78059 * The message is freed if and only if its contents are successfully copied
9945181Sgd78059 * and queued for transmission (so that the return value is B_TRUE).
9955181Sgd78059 * If we can't queue the message, the return value is B_FALSE and
9965181Sgd78059 * the message is *not* freed.
9975181Sgd78059 *
9985181Sgd78059 * This routine handles the special case of <mp> == NULL, which indicates
9995181Sgd78059 * that we want to "send" the special "setup packet" allocated during
10005181Sgd78059 * startup. We have to use some different flags in the packet descriptor
10015181Sgd78059 * to say its a setup packet (from the global <dmfe_setup_desc1>), and the
10025181Sgd78059 * setup packet *isn't* freed after use.
10035181Sgd78059 */
10045181Sgd78059 static boolean_t
dmfe_send_msg(dmfe_t * dmfep,mblk_t * mp)10055181Sgd78059 dmfe_send_msg(dmfe_t *dmfep, mblk_t *mp)
10065181Sgd78059 {
10075181Sgd78059 dma_area_t *descp;
10085181Sgd78059 mblk_t *bp;
10095181Sgd78059 char *txb;
10105181Sgd78059 uint32_t desc1;
10115181Sgd78059 uint32_t index;
10125181Sgd78059 size_t totlen;
10135181Sgd78059 size_t mblen;
10149860Sgdamore@opensolaris.org uint32_t paddr;
10155181Sgd78059
10165181Sgd78059 /*
10175181Sgd78059 * If the number of free slots is below the reclaim threshold
10185181Sgd78059 * (soft limit), we'll try to reclaim some. If we fail, and
10195181Sgd78059 * the number of free slots is also below the minimum required
10205181Sgd78059 * (the hard limit, usually 1), then we can't send the packet.
10215181Sgd78059 */
10225181Sgd78059 mutex_enter(dmfep->txlock);
10239860Sgdamore@opensolaris.org if (dmfep->suspended)
10249860Sgdamore@opensolaris.org return (B_FALSE);
10259860Sgdamore@opensolaris.org
10265181Sgd78059 if (dmfep->tx.n_free <= dmfe_tx_reclaim_level &&
10275181Sgd78059 dmfe_reclaim_tx_desc(dmfep) == B_FALSE &&
10285181Sgd78059 dmfep->tx.n_free <= dmfe_tx_min_free) {
10295181Sgd78059 /*
10305181Sgd78059 * Resource shortage - return B_FALSE so the packet
10315181Sgd78059 * will be queued for retry after the next TX-done
10325181Sgd78059 * interrupt.
10335181Sgd78059 */
10345181Sgd78059 mutex_exit(dmfep->txlock);
10359860Sgdamore@opensolaris.org DTRACE_PROBE(tx__no__desc);
10365181Sgd78059 return (B_FALSE);
10375181Sgd78059 }
10385181Sgd78059
10395181Sgd78059 /*
10405181Sgd78059 * There's a slot available, so claim it by incrementing
10415181Sgd78059 * the next-free index and decrementing the free count.
10425181Sgd78059 * If the ring is currently empty, we also restart the
10435181Sgd78059 * stall-detect timer. The ASSERTions check that our
10445181Sgd78059 * invariants still hold:
10455181Sgd78059 * the next-free index must not match the next-busy index
10465181Sgd78059 * there must still be at least one free entry
10475181Sgd78059 * After this, we now have exclusive ownership of the ring
10485181Sgd78059 * entry (and matching buffer) indicated by <index>, so we
10495181Sgd78059 * don't need to hold the TX lock any longer
10505181Sgd78059 */
10515181Sgd78059 index = dmfep->tx.next_free;
10525181Sgd78059 dmfep->tx.next_free = NEXT(index, dmfep->tx.n_desc);
10535181Sgd78059 ASSERT(dmfep->tx.next_free != dmfep->tx.next_busy);
10545181Sgd78059 if (dmfep->tx.n_free-- == dmfep->tx.n_desc)
10555181Sgd78059 dmfep->tx_pending_tix = 0;
10565181Sgd78059 ASSERT(dmfep->tx.n_free >= 1);
10575181Sgd78059 mutex_exit(dmfep->txlock);
10585181Sgd78059
10595181Sgd78059 /*
10605181Sgd78059 * Check the ownership of the ring entry ...
10615181Sgd78059 */
10625181Sgd78059 descp = &dmfep->tx_desc;
10635181Sgd78059 ASSERT((dmfe_ring_get32(descp, index, DESC0) & TX_OWN) == 0);
10645181Sgd78059
10655181Sgd78059 if (mp == NULL) {
10665181Sgd78059 /*
10675181Sgd78059 * Indicates we should send a SETUP packet, which we do by
10685181Sgd78059 * temporarily switching the BUFFER1 pointer in the ring
10695181Sgd78059 * entry. The reclaim routine will restore BUFFER1 to its
10705181Sgd78059 * usual value.
10715181Sgd78059 *
10725181Sgd78059 * Note that as the setup packet is tagged on the end of
10735181Sgd78059 * the TX ring, when we sync the descriptor we're also
10745181Sgd78059 * implicitly syncing the setup packet - hence, we don't
10755181Sgd78059 * need a separate ddi_dma_sync() call here.
10765181Sgd78059 */
10775181Sgd78059 desc1 = dmfe_setup_desc1;
10789860Sgdamore@opensolaris.org paddr = descp->setup_dvma;
10795181Sgd78059 } else {
10805181Sgd78059 /*
10815181Sgd78059 * A regular packet; we copy the data into a pre-mapped
10825181Sgd78059 * buffer, which avoids the overhead (and complication)
10835181Sgd78059 * of mapping/unmapping STREAMS buffers and keeping hold
10845181Sgd78059 * of them until the DMA has completed.
10855181Sgd78059 *
10865181Sgd78059 * Because all buffers are the same size, and larger
10875181Sgd78059 * than the longest single valid message, we don't have
10885181Sgd78059 * to bother about splitting the message across multiple
10895181Sgd78059 * buffers.
10905181Sgd78059 */
10915181Sgd78059 txb = &dmfep->tx_buff.mem_va[index*DMFE_BUF_SIZE];
10925181Sgd78059 totlen = 0;
10935181Sgd78059 bp = mp;
10945181Sgd78059
10955181Sgd78059 /*
10965181Sgd78059 * Copy all (remaining) mblks in the message ...
10975181Sgd78059 */
10985181Sgd78059 for (; bp != NULL; bp = bp->b_cont) {
10996990Sgd78059 mblen = MBLKL(bp);
11005181Sgd78059 if ((totlen += mblen) <= DMFE_MAX_PKT_SIZE) {
11015181Sgd78059 bcopy(bp->b_rptr, txb, mblen);
11025181Sgd78059 txb += mblen;
11035181Sgd78059 }
11045181Sgd78059 }
11055181Sgd78059
11065181Sgd78059 /*
11075181Sgd78059 * Is this a multicast or broadcast packet? We do
11085181Sgd78059 * this so that we can track statistics accurately
11095181Sgd78059 * when we reclaim it.
11105181Sgd78059 */
11115181Sgd78059 txb = &dmfep->tx_buff.mem_va[index*DMFE_BUF_SIZE];
11125181Sgd78059 if (txb[0] & 0x1) {
11135181Sgd78059 if (bcmp(txb, dmfe_broadcast_addr, ETHERADDRL) == 0) {
11145181Sgd78059 dmfep->tx_bcast[index / NBBY] |=
11155181Sgd78059 (1 << (index % NBBY));
11165181Sgd78059 } else {
11175181Sgd78059 dmfep->tx_mcast[index / NBBY] |=
11185181Sgd78059 (1 << (index % NBBY));
11195181Sgd78059 }
11205181Sgd78059 }
11215181Sgd78059
11225181Sgd78059 /*
11235181Sgd78059 * We'e reached the end of the chain; and we should have
11245181Sgd78059 * collected no more than DMFE_MAX_PKT_SIZE bytes into our
11255181Sgd78059 * buffer. Note that the <size> field in the descriptor is
11265181Sgd78059 * only 11 bits, so bigger packets would be a problem!
11275181Sgd78059 */
11285181Sgd78059 ASSERT(bp == NULL);
11295181Sgd78059 ASSERT(totlen <= DMFE_MAX_PKT_SIZE);
11305181Sgd78059 totlen &= TX_BUFFER_SIZE1;
11315181Sgd78059 desc1 = TX_FIRST_DESC | TX_LAST_DESC | totlen;
11329860Sgdamore@opensolaris.org paddr = dmfep->tx_buff.mem_dvma + index*DMFE_BUF_SIZE;
11335181Sgd78059
11345181Sgd78059 (void) ddi_dma_sync(dmfep->tx_buff.dma_hdl,
11355181Sgd78059 index * DMFE_BUF_SIZE, DMFE_BUF_SIZE, DDI_DMA_SYNC_FORDEV);
11365181Sgd78059 }
11375181Sgd78059
11385181Sgd78059 /*
11395181Sgd78059 * Update ring descriptor entries, sync them, and wake up the
11405181Sgd78059 * transmit process
11415181Sgd78059 */
11425181Sgd78059 if ((index & dmfe_tx_int_factor) == 0)
11435181Sgd78059 desc1 |= TX_INT_ON_COMP;
11445181Sgd78059 desc1 |= TX_CHAINING;
11459860Sgdamore@opensolaris.org dmfe_ring_put32(descp, index, BUFFER1, paddr);
11465181Sgd78059 dmfe_ring_put32(descp, index, DESC1, desc1);
11475181Sgd78059 dmfe_ring_put32(descp, index, DESC0, TX_OWN);
11485181Sgd78059 DMA_SYNC(descp, DDI_DMA_SYNC_FORDEV);
11495181Sgd78059 dmfe_chip_put32(dmfep, TX_POLL_REG, 0);
11505181Sgd78059
11515181Sgd78059 /*
11525181Sgd78059 * Finally, free the message & return success
11535181Sgd78059 */
11545181Sgd78059 if (mp)
11555181Sgd78059 freemsg(mp);
11565181Sgd78059 return (B_TRUE);
11575181Sgd78059 }
11585181Sgd78059
11595181Sgd78059 /*
11605181Sgd78059 * dmfe_m_tx() -- send a chain of packets
11615181Sgd78059 *
11625181Sgd78059 * Called when packet(s) are ready to be transmitted. A pointer to an
11635181Sgd78059 * M_DATA message that contains the packet is passed to this routine.
11645181Sgd78059 * The complete LLC header is contained in the message's first message
11655181Sgd78059 * block, and the remainder of the packet is contained within
11665181Sgd78059 * additional M_DATA message blocks linked to the first message block.
11675181Sgd78059 *
11685181Sgd78059 * Additional messages may be passed by linking with b_next.
11695181Sgd78059 */
11705181Sgd78059 static mblk_t *
dmfe_m_tx(void * arg,mblk_t * mp)11715181Sgd78059 dmfe_m_tx(void *arg, mblk_t *mp)
11725181Sgd78059 {
11735181Sgd78059 dmfe_t *dmfep = arg; /* private device info */
11745181Sgd78059 mblk_t *next;
11755181Sgd78059
11765181Sgd78059 ASSERT(mp != NULL);
11775181Sgd78059 ASSERT(dmfep->mac_state == DMFE_MAC_STARTED);
11785181Sgd78059
11795181Sgd78059 if (dmfep->chip_state != CHIP_RUNNING)
11805181Sgd78059 return (mp);
11815181Sgd78059
11825181Sgd78059 while (mp != NULL) {
11835181Sgd78059 next = mp->b_next;
11845181Sgd78059 mp->b_next = NULL;
11855181Sgd78059 if (!dmfe_send_msg(dmfep, mp)) {
11865181Sgd78059 mp->b_next = next;
11875181Sgd78059 break;
11885181Sgd78059 }
11895181Sgd78059 mp = next;
11905181Sgd78059 }
11915181Sgd78059
11925181Sgd78059 return (mp);
11935181Sgd78059 }
11945181Sgd78059
11955181Sgd78059 /*
11965181Sgd78059 * ========== Address-setting routines (TX-side) ==========
11975181Sgd78059 */
11985181Sgd78059
11995181Sgd78059 /*
12005181Sgd78059 * Find the index of the relevant bit in the setup packet.
12015181Sgd78059 * This must mirror the way the hardware will actually calculate it!
12025181Sgd78059 */
12035181Sgd78059 static uint32_t
dmfe_hash_index(const uint8_t * address)12045181Sgd78059 dmfe_hash_index(const uint8_t *address)
12055181Sgd78059 {
12065181Sgd78059 uint32_t const POLY = HASH_POLY;
12075181Sgd78059 uint32_t crc = HASH_CRC;
12085181Sgd78059 uint32_t index;
12095181Sgd78059 uint32_t msb;
12105181Sgd78059 uchar_t currentbyte;
12115181Sgd78059 int byteslength;
12125181Sgd78059 int shift;
12135181Sgd78059 int bit;
12145181Sgd78059
12155181Sgd78059 for (byteslength = 0; byteslength < ETHERADDRL; ++byteslength) {
12165181Sgd78059 currentbyte = address[byteslength];
12175181Sgd78059 for (bit = 0; bit < 8; ++bit) {
12185181Sgd78059 msb = crc >> 31;
12195181Sgd78059 crc <<= 1;
12205181Sgd78059 if (msb ^ (currentbyte & 1)) {
12215181Sgd78059 crc ^= POLY;
12225181Sgd78059 crc |= 0x00000001;
12235181Sgd78059 }
12245181Sgd78059 currentbyte >>= 1;
12255181Sgd78059 }
12265181Sgd78059 }
12275181Sgd78059
12285181Sgd78059 for (index = 0, bit = 23, shift = 8; shift >= 0; ++bit, --shift)
12295181Sgd78059 index |= (((crc >> bit) & 1) << shift);
12305181Sgd78059
12315181Sgd78059 return (index);
12325181Sgd78059 }
12335181Sgd78059
12345181Sgd78059 /*
12355181Sgd78059 * Find and set/clear the relevant bit in the setup packet hash table
12365181Sgd78059 * This must mirror the way the hardware will actually interpret it!
12375181Sgd78059 */
12385181Sgd78059 static void
dmfe_update_hash(dmfe_t * dmfep,uint32_t index,boolean_t val)12395181Sgd78059 dmfe_update_hash(dmfe_t *dmfep, uint32_t index, boolean_t val)
12405181Sgd78059 {
12415181Sgd78059 dma_area_t *descp;
12425181Sgd78059 uint32_t tmp;
12435181Sgd78059
12445181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
12455181Sgd78059
12465181Sgd78059 descp = &dmfep->tx_desc;
12475181Sgd78059 tmp = dmfe_setup_get32(descp, index/16);
12485181Sgd78059 if (val)
12495181Sgd78059 tmp |= 1 << (index%16);
12505181Sgd78059 else
12515181Sgd78059 tmp &= ~(1 << (index%16));
12525181Sgd78059 dmfe_setup_put32(descp, index/16, tmp);
12535181Sgd78059 }
12545181Sgd78059
12555181Sgd78059 /*
12565181Sgd78059 * Update the refcount for the bit in the setup packet corresponding
12575181Sgd78059 * to the specified address; if it changes between zero & nonzero,
12585181Sgd78059 * also update the bitmap itself & return B_TRUE, so that the caller
12595181Sgd78059 * knows to re-send the setup packet. Otherwise (only the refcount
12605181Sgd78059 * changed), return B_FALSE
12615181Sgd78059 */
12625181Sgd78059 static boolean_t
dmfe_update_mcast(dmfe_t * dmfep,const uint8_t * mca,boolean_t val)12635181Sgd78059 dmfe_update_mcast(dmfe_t *dmfep, const uint8_t *mca, boolean_t val)
12645181Sgd78059 {
12655181Sgd78059 uint32_t index;
12665181Sgd78059 uint8_t *refp;
12675181Sgd78059 boolean_t change;
12685181Sgd78059
12695181Sgd78059 index = dmfe_hash_index(mca);
12705181Sgd78059 refp = &dmfep->mcast_refs[index];
12715181Sgd78059 change = (val ? (*refp)++ : --(*refp)) == 0;
12725181Sgd78059
12735181Sgd78059 if (change)
12745181Sgd78059 dmfe_update_hash(dmfep, index, val);
12755181Sgd78059
12765181Sgd78059 return (change);
12775181Sgd78059 }
12785181Sgd78059
12795181Sgd78059 /*
12805181Sgd78059 * "Transmit" the (possibly updated) magic setup packet
12815181Sgd78059 */
12825181Sgd78059 static int
dmfe_send_setup(dmfe_t * dmfep)12835181Sgd78059 dmfe_send_setup(dmfe_t *dmfep)
12845181Sgd78059 {
12855181Sgd78059 int status;
12865181Sgd78059
12875181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
12885181Sgd78059
12899860Sgdamore@opensolaris.org if (dmfep->suspended)
12909860Sgdamore@opensolaris.org return (0);
12919860Sgdamore@opensolaris.org
12925181Sgd78059 /*
12935181Sgd78059 * If the chip isn't running, we can't really send the setup frame
12945181Sgd78059 * now but it doesn't matter, 'cos it will be sent when the transmit
12955181Sgd78059 * process is restarted (see dmfe_start()).
12965181Sgd78059 */
12975181Sgd78059 if ((dmfep->opmode & START_TRANSMIT) == 0)
12985181Sgd78059 return (0);
12995181Sgd78059
13005181Sgd78059 /*
13015181Sgd78059 * "Send" the setup frame. If it fails (e.g. no resources),
13025181Sgd78059 * set a flag; then the factotum will retry the "send". Once
13035181Sgd78059 * it works, we can clear the flag no matter how many attempts
13045181Sgd78059 * had previously failed. We tell the caller that it worked
13055181Sgd78059 * whether it did or not; after all, it *will* work eventually.
13065181Sgd78059 */
13075181Sgd78059 status = dmfe_send_msg(dmfep, NULL);
13085181Sgd78059 dmfep->need_setup = status ? B_FALSE : B_TRUE;
13095181Sgd78059 return (0);
13105181Sgd78059 }
13115181Sgd78059
13125181Sgd78059 /*
13135181Sgd78059 * dmfe_m_unicst() -- set the physical network address
13145181Sgd78059 */
13155181Sgd78059 static int
dmfe_m_unicst(void * arg,const uint8_t * macaddr)13165181Sgd78059 dmfe_m_unicst(void *arg, const uint8_t *macaddr)
13175181Sgd78059 {
13185181Sgd78059 dmfe_t *dmfep = arg;
13195181Sgd78059 int status;
13205181Sgd78059 int index;
13215181Sgd78059
13225181Sgd78059 /*
13235181Sgd78059 * Update our current address and send out a new setup packet
13245181Sgd78059 *
13255181Sgd78059 * Here we accommodate the use of HASH_ONLY or HASH_AND_PERFECT
13265181Sgd78059 * filtering modes (we don't support PERFECT_ONLY or INVERSE modes).
13275181Sgd78059 *
13285181Sgd78059 * It is said that there is a bug in the 21140 where it fails to
13295181Sgd78059 * receive packes addresses to the specified perfect filter address.
13305181Sgd78059 * If the same bug is present in the DM9102A, the TX_FILTER_TYPE1
13315181Sgd78059 * bit should be set in the module variable dmfe_setup_desc1.
13325181Sgd78059 *
13335181Sgd78059 * If TX_FILTER_TYPE1 is set, we will use HASH_ONLY filtering.
13345181Sgd78059 * In this mode, *all* incoming addresses are hashed and looked
13355181Sgd78059 * up in the bitmap described by the setup packet. Therefore,
13365181Sgd78059 * the bit representing the station address has to be added to
13375181Sgd78059 * the table before sending it out. If the address is changed,
13385181Sgd78059 * the old entry should be removed before the new entry is made.
13395181Sgd78059 *
13405181Sgd78059 * NOTE: in this mode, unicast packets that are not intended for
13415181Sgd78059 * this station may be received; it is up to software to filter
13425181Sgd78059 * them out afterwards!
13435181Sgd78059 *
13445181Sgd78059 * If TX_FILTER_TYPE1 is *not* set, we will use HASH_AND_PERFECT
13455181Sgd78059 * filtering. In this mode, multicast addresses are hashed and
13465181Sgd78059 * checked against the bitmap, while unicast addresses are simply
13475181Sgd78059 * matched against the one physical address specified in the setup
13485181Sgd78059 * packet. This means that we shouldn't receive unicast packets
13495181Sgd78059 * that aren't intended for us (but software still has to filter
13505181Sgd78059 * multicast packets just the same).
13515181Sgd78059 *
13525181Sgd78059 * Whichever mode we're using, we have to enter the broadcast
13535181Sgd78059 * address into the multicast filter map too, so we do this on
13545181Sgd78059 * the first time through after attach or reset.
13555181Sgd78059 */
13565181Sgd78059 mutex_enter(dmfep->oplock);
13575181Sgd78059
13585181Sgd78059 if (dmfep->addr_set && dmfe_setup_desc1 & TX_FILTER_TYPE1)
13595181Sgd78059 (void) dmfe_update_mcast(dmfep, dmfep->curr_addr, B_FALSE);
13605181Sgd78059 if (dmfe_setup_desc1 & TX_FILTER_TYPE1)
13615181Sgd78059 (void) dmfe_update_mcast(dmfep, macaddr, B_TRUE);
13625181Sgd78059 if (!dmfep->addr_set)
13635181Sgd78059 (void) dmfe_update_mcast(dmfep, dmfe_broadcast_addr, B_TRUE);
13645181Sgd78059
13655181Sgd78059 /*
13665181Sgd78059 * Remember the new current address
13675181Sgd78059 */
13685181Sgd78059 ethaddr_copy(macaddr, dmfep->curr_addr);
13695181Sgd78059 dmfep->addr_set = B_TRUE;
13705181Sgd78059
13715181Sgd78059 /*
13725181Sgd78059 * Install the new physical address into the proper position in
13735181Sgd78059 * the setup frame; this is only used if we select hash+perfect
13745181Sgd78059 * filtering, but we'll put it in anyway. The ugliness here is
13755181Sgd78059 * down to the usual war of the egg :(
13765181Sgd78059 */
13775181Sgd78059 for (index = 0; index < ETHERADDRL; index += 2)
13785181Sgd78059 dmfe_setup_put32(&dmfep->tx_desc, SETUPBUF_PHYS+index/2,
13795181Sgd78059 (macaddr[index+1] << 8) | macaddr[index]);
13805181Sgd78059
13815181Sgd78059 /*
13825181Sgd78059 * Finally, we're ready to "transmit" the setup frame
13835181Sgd78059 */
13845181Sgd78059 status = dmfe_send_setup(dmfep);
13855181Sgd78059 mutex_exit(dmfep->oplock);
13865181Sgd78059
13875181Sgd78059 return (status);
13885181Sgd78059 }
13895181Sgd78059
13905181Sgd78059 /*
13915181Sgd78059 * dmfe_m_multicst() -- enable or disable a multicast address
13925181Sgd78059 *
13935181Sgd78059 * Program the hardware to enable/disable the multicast address
13945181Sgd78059 * in "mca" (enable if add is true, otherwise disable it.)
13955181Sgd78059 * We keep a refcount for each bit in the map, so that it still
13965181Sgd78059 * works out properly if multiple addresses hash to the same bit.
13975181Sgd78059 * dmfe_update_mcast() tells us whether the map actually changed;
13985181Sgd78059 * if so, we have to re-"transmit" the magic setup packet.
13995181Sgd78059 */
14005181Sgd78059 static int
dmfe_m_multicst(void * arg,boolean_t add,const uint8_t * mca)14015181Sgd78059 dmfe_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
14025181Sgd78059 {
14035181Sgd78059 dmfe_t *dmfep = arg; /* private device info */
14045181Sgd78059 int status = 0;
14055181Sgd78059
14065181Sgd78059 mutex_enter(dmfep->oplock);
14075181Sgd78059 if (dmfe_update_mcast(dmfep, mca, add))
14085181Sgd78059 status = dmfe_send_setup(dmfep);
14095181Sgd78059 mutex_exit(dmfep->oplock);
14105181Sgd78059
14115181Sgd78059 return (status);
14125181Sgd78059 }
14135181Sgd78059
14145181Sgd78059
14155181Sgd78059 /*
14165181Sgd78059 * ========== Internal state management entry points ==========
14175181Sgd78059 */
14185181Sgd78059
14195181Sgd78059 /*
14205181Sgd78059 * These routines provide all the functionality required by the
14215181Sgd78059 * corresponding MAC layer entry points, but don't update the MAC layer state
14225181Sgd78059 * so they can be called internally without disturbing our record
14235181Sgd78059 * of what MAC layer thinks we should be doing ...
14245181Sgd78059 */
14255181Sgd78059
14265181Sgd78059 /*
14275181Sgd78059 * dmfe_stop() -- stop processing, don't reset h/w or rings
14285181Sgd78059 */
14295181Sgd78059 static void
dmfe_stop(dmfe_t * dmfep)14305181Sgd78059 dmfe_stop(dmfe_t *dmfep)
14315181Sgd78059 {
14325181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
14335181Sgd78059
14345181Sgd78059 dmfe_stop_chip(dmfep, CHIP_STOPPED);
14355181Sgd78059 }
14365181Sgd78059
14375181Sgd78059 /*
14385181Sgd78059 * dmfe_reset() -- stop processing, reset h/w & rings to initial state
14395181Sgd78059 */
14405181Sgd78059 static void
dmfe_reset(dmfe_t * dmfep)14415181Sgd78059 dmfe_reset(dmfe_t *dmfep)
14425181Sgd78059 {
14435181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
14445181Sgd78059 ASSERT(mutex_owned(dmfep->rxlock));
14455181Sgd78059 ASSERT(mutex_owned(dmfep->txlock));
14465181Sgd78059
14475181Sgd78059 dmfe_stop_chip(dmfep, CHIP_RESET);
14485181Sgd78059 dmfe_init_rings(dmfep);
14495181Sgd78059 }
14505181Sgd78059
14515181Sgd78059 /*
14525181Sgd78059 * dmfe_start() -- start transmitting/receiving
14535181Sgd78059 */
14545181Sgd78059 static void
dmfe_start(dmfe_t * dmfep)14555181Sgd78059 dmfe_start(dmfe_t *dmfep)
14565181Sgd78059 {
14575181Sgd78059 uint32_t gpsr;
14585181Sgd78059
14595181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
14605181Sgd78059
14615181Sgd78059 ASSERT(dmfep->chip_state == CHIP_RESET ||
14625181Sgd78059 dmfep->chip_state == CHIP_STOPPED);
14635181Sgd78059
14645181Sgd78059 /*
14655181Sgd78059 * Make opmode consistent with PHY duplex setting
14665181Sgd78059 */
14675181Sgd78059 gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
14685181Sgd78059 if (gpsr & GPS_FULL_DUPLEX)
14695181Sgd78059 dmfep->opmode |= FULL_DUPLEX;
14705181Sgd78059 else
14715181Sgd78059 dmfep->opmode &= ~FULL_DUPLEX;
14725181Sgd78059
14735181Sgd78059 /*
14745181Sgd78059 * Start transmit processing
14755181Sgd78059 * Set up the address filters
14765181Sgd78059 * Start receive processing
14775181Sgd78059 * Enable interrupts
14785181Sgd78059 */
14795181Sgd78059 dmfe_start_chip(dmfep, START_TRANSMIT);
14805181Sgd78059 (void) dmfe_send_setup(dmfep);
14815181Sgd78059 drv_usecwait(10);
14825181Sgd78059 dmfe_start_chip(dmfep, START_RECEIVE);
14835181Sgd78059 dmfe_enable_interrupts(dmfep);
14845181Sgd78059 }
14855181Sgd78059
14865181Sgd78059 /*
14875181Sgd78059 * dmfe_restart - restart transmitting/receiving after error or suspend
14885181Sgd78059 */
14895181Sgd78059 static void
dmfe_restart(dmfe_t * dmfep)14905181Sgd78059 dmfe_restart(dmfe_t *dmfep)
14915181Sgd78059 {
14925181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
14935181Sgd78059
14945181Sgd78059 /*
14955181Sgd78059 * You need not only <oplock>, but also <rxlock> AND <txlock>
14965181Sgd78059 * in order to reset the rings, but then <txlock> *mustn't*
14975181Sgd78059 * be held across the call to dmfe_start()
14985181Sgd78059 */
14995181Sgd78059 mutex_enter(dmfep->rxlock);
15005181Sgd78059 mutex_enter(dmfep->txlock);
15015181Sgd78059 dmfe_reset(dmfep);
15025181Sgd78059 mutex_exit(dmfep->txlock);
15035181Sgd78059 mutex_exit(dmfep->rxlock);
15049860Sgdamore@opensolaris.org if (dmfep->mac_state == DMFE_MAC_STARTED) {
15055181Sgd78059 dmfe_start(dmfep);
15069860Sgdamore@opensolaris.org }
15075181Sgd78059 }
15085181Sgd78059
15095181Sgd78059
15105181Sgd78059 /*
15115181Sgd78059 * ========== MAC-required management entry points ==========
15125181Sgd78059 */
15135181Sgd78059
15145181Sgd78059 /*
15155181Sgd78059 * dmfe_m_stop() -- stop transmitting/receiving
15165181Sgd78059 */
15175181Sgd78059 static void
dmfe_m_stop(void * arg)15185181Sgd78059 dmfe_m_stop(void *arg)
15195181Sgd78059 {
15205181Sgd78059 dmfe_t *dmfep = arg; /* private device info */
15215181Sgd78059
15225181Sgd78059 /*
15235181Sgd78059 * Just stop processing, then record new MAC state
15245181Sgd78059 */
15259860Sgdamore@opensolaris.org mii_stop(dmfep->mii);
15269860Sgdamore@opensolaris.org
15275181Sgd78059 mutex_enter(dmfep->oplock);
15289860Sgdamore@opensolaris.org if (!dmfep->suspended)
15299860Sgdamore@opensolaris.org dmfe_stop(dmfep);
15305181Sgd78059 dmfep->mac_state = DMFE_MAC_STOPPED;
15315181Sgd78059 mutex_exit(dmfep->oplock);
15325181Sgd78059 }
15335181Sgd78059
15345181Sgd78059 /*
15355181Sgd78059 * dmfe_m_start() -- start transmitting/receiving
15365181Sgd78059 */
15375181Sgd78059 static int
dmfe_m_start(void * arg)15385181Sgd78059 dmfe_m_start(void *arg)
15395181Sgd78059 {
15405181Sgd78059 dmfe_t *dmfep = arg; /* private device info */
15415181Sgd78059
15425181Sgd78059 /*
15435181Sgd78059 * Start processing and record new MAC state
15445181Sgd78059 */
15455181Sgd78059 mutex_enter(dmfep->oplock);
15469860Sgdamore@opensolaris.org if (!dmfep->suspended)
15479860Sgdamore@opensolaris.org dmfe_start(dmfep);
15485181Sgd78059 dmfep->mac_state = DMFE_MAC_STARTED;
15495181Sgd78059 mutex_exit(dmfep->oplock);
15505181Sgd78059
15519860Sgdamore@opensolaris.org mii_start(dmfep->mii);
15529860Sgdamore@opensolaris.org
15535181Sgd78059 return (0);
15545181Sgd78059 }
15555181Sgd78059
15565181Sgd78059 /*
15575181Sgd78059 * dmfe_m_promisc() -- set or reset promiscuous mode on the board
15585181Sgd78059 *
15595181Sgd78059 * Program the hardware to enable/disable promiscuous and/or
15605181Sgd78059 * receive-all-multicast modes. Davicom don't document this
15615181Sgd78059 * clearly, but it looks like we can do this on-the-fly (i.e.
15625181Sgd78059 * without stopping & restarting the TX/RX processes).
15635181Sgd78059 */
15645181Sgd78059 static int
dmfe_m_promisc(void * arg,boolean_t on)15655181Sgd78059 dmfe_m_promisc(void *arg, boolean_t on)
15665181Sgd78059 {
15675181Sgd78059 dmfe_t *dmfep = arg;
15685181Sgd78059
15695181Sgd78059 mutex_enter(dmfep->oplock);
15705181Sgd78059 dmfep->opmode &= ~(PROMISC_MODE | PASS_MULTICAST);
15715181Sgd78059 if (on)
15725181Sgd78059 dmfep->opmode |= PROMISC_MODE;
15739860Sgdamore@opensolaris.org if (!dmfep->suspended)
15749860Sgdamore@opensolaris.org dmfe_set_opmode(dmfep);
15755181Sgd78059 mutex_exit(dmfep->oplock);
15765181Sgd78059
15775181Sgd78059 return (0);
15785181Sgd78059 }
15795181Sgd78059
15805181Sgd78059 /*
15815181Sgd78059 * ========== Factotum, implemented as a softint handler ==========
15825181Sgd78059 */
15835181Sgd78059
15845181Sgd78059 /*
15855181Sgd78059 * The factotum is woken up when there's something to do that we'd rather
15865181Sgd78059 * not do from inside a (high-level?) hardware interrupt handler. Its
15875181Sgd78059 * two main tasks are:
15885181Sgd78059 * reset & restart the chip after an error
15895181Sgd78059 * update & restart the chip after a link status change
15905181Sgd78059 */
15915181Sgd78059 static uint_t
dmfe_factotum(caddr_t arg)15925181Sgd78059 dmfe_factotum(caddr_t arg)
15935181Sgd78059 {
15945181Sgd78059 dmfe_t *dmfep;
15955181Sgd78059
15966990Sgd78059 dmfep = (void *)arg;
15975181Sgd78059 ASSERT(dmfep->dmfe_guard == DMFE_GUARD);
15985181Sgd78059
15995181Sgd78059 mutex_enter(dmfep->oplock);
16009860Sgdamore@opensolaris.org if (dmfep->suspended) {
16019860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
16029860Sgdamore@opensolaris.org return (DDI_INTR_CLAIMED);
16039860Sgdamore@opensolaris.org }
16045181Sgd78059
16055181Sgd78059 dmfep->factotum_flag = 0;
16065181Sgd78059 DRV_KS_INC(dmfep, KS_FACTOTUM_RUN);
16075181Sgd78059
16085181Sgd78059 /*
16095181Sgd78059 * Check for chip error ...
16105181Sgd78059 */
16115181Sgd78059 if (dmfep->chip_state == CHIP_ERROR) {
16125181Sgd78059 /*
16135181Sgd78059 * Error recovery required: reset the chip and the rings,
16145181Sgd78059 * then, if it's supposed to be running, kick it off again.
16155181Sgd78059 */
16165181Sgd78059 DRV_KS_INC(dmfep, KS_RECOVERY);
16175181Sgd78059 dmfe_restart(dmfep);
16189860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
16199860Sgdamore@opensolaris.org
16209860Sgdamore@opensolaris.org mii_reset(dmfep->mii);
16219860Sgdamore@opensolaris.org
16225181Sgd78059 } else if (dmfep->need_setup) {
16235181Sgd78059 (void) dmfe_send_setup(dmfep);
16245181Sgd78059 mutex_exit(dmfep->oplock);
16255181Sgd78059 }
16265181Sgd78059
16275181Sgd78059 return (DDI_INTR_CLAIMED);
16285181Sgd78059 }
16295181Sgd78059
16305181Sgd78059 static void
dmfe_wake_factotum(dmfe_t * dmfep,int ks_id,const char * why)16315181Sgd78059 dmfe_wake_factotum(dmfe_t *dmfep, int ks_id, const char *why)
16325181Sgd78059 {
16339860Sgdamore@opensolaris.org _NOTE(ARGUNUSED(why));
16345181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
16355181Sgd78059 DRV_KS_INC(dmfep, ks_id);
16365181Sgd78059
16375181Sgd78059 if (dmfep->factotum_flag++ == 0)
16385181Sgd78059 ddi_trigger_softintr(dmfep->factotum_id);
16395181Sgd78059 }
16405181Sgd78059
16415181Sgd78059
16425181Sgd78059 /*
16435181Sgd78059 * ========== Periodic Tasks (Cyclic handler & friends) ==========
16445181Sgd78059 */
16455181Sgd78059
16465181Sgd78059 /*
16475181Sgd78059 * Periodic tick tasks, run from the cyclic handler
16485181Sgd78059 *
16495181Sgd78059 * Check for TX stall; flag an error and wake the factotum if so.
16505181Sgd78059 */
16515181Sgd78059 static void
dmfe_tick_stall_check(dmfe_t * dmfep,uint32_t gpsr,uint32_t istat)16525181Sgd78059 dmfe_tick_stall_check(dmfe_t *dmfep, uint32_t gpsr, uint32_t istat)
16535181Sgd78059 {
16545181Sgd78059 boolean_t tx_stall;
16555181Sgd78059 uint32_t tx_state;
16565181Sgd78059 uint32_t limit;
16575181Sgd78059
16585181Sgd78059 ASSERT(mutex_owned(dmfep->oplock));
16595181Sgd78059
16605181Sgd78059 /*
16615181Sgd78059 * Check for transmit stall ...
16625181Sgd78059 *
16635181Sgd78059 * IF there's at least one packet in the ring, AND the timeout
16645181Sgd78059 * has elapsed, AND we can't reclaim any descriptors, THEN we've
16655181Sgd78059 * stalled; we return B_TRUE to trigger a reset-and-recover cycle.
16665181Sgd78059 *
16675181Sgd78059 * Note that the timeout limit is based on the transmit engine
16685181Sgd78059 * state; we allow the transmitter longer to make progress in
16695181Sgd78059 * some states than in others, based on observations of this
16705181Sgd78059 * chip's actual behaviour in the lab.
16715181Sgd78059 *
16725181Sgd78059 * By observation, we find that on about 1 in 10000 passes through
16735181Sgd78059 * here, the TX lock is already held. In that case, we'll skip
16745181Sgd78059 * the check on this pass rather than wait. Most likely, the send
16755181Sgd78059 * routine was holding the lock when the interrupt happened, and
16765181Sgd78059 * we'll succeed next time through. In the event of a real stall,
16775181Sgd78059 * the TX ring will fill up, after which the send routine won't be
16785181Sgd78059 * called any more and then we're sure to get in.
16795181Sgd78059 */
16805181Sgd78059 tx_stall = B_FALSE;
16815181Sgd78059 if (mutex_tryenter(dmfep->txlock)) {
16825181Sgd78059 if (dmfep->tx.n_free < dmfep->tx.n_desc) {
16835181Sgd78059 tx_state = TX_PROCESS_STATE(istat);
16845181Sgd78059 if (gpsr & GPS_LINK_100)
16855181Sgd78059 limit = stall_100_tix[tx_state];
16865181Sgd78059 else
16875181Sgd78059 limit = stall_10_tix[tx_state];
16885181Sgd78059 if (++dmfep->tx_pending_tix >= limit &&
16895181Sgd78059 dmfe_reclaim_tx_desc(dmfep) == B_FALSE) {
16905181Sgd78059 dmfe_log(dmfep, "TX stall detected "
16915181Sgd78059 "after %d ticks in state %d; "
16925181Sgd78059 "automatic recovery initiated",
16935181Sgd78059 dmfep->tx_pending_tix, tx_state);
16945181Sgd78059 tx_stall = B_TRUE;
16955181Sgd78059 }
16965181Sgd78059 }
16975181Sgd78059 mutex_exit(dmfep->txlock);
16985181Sgd78059 }
16995181Sgd78059
17005181Sgd78059 if (tx_stall) {
17015181Sgd78059 dmfe_stop_chip(dmfep, CHIP_ERROR);
17025181Sgd78059 dmfe_wake_factotum(dmfep, KS_TX_STALL, "tick (TX stall)");
17035181Sgd78059 }
17045181Sgd78059 }
17055181Sgd78059
17065181Sgd78059 /*
17075181Sgd78059 * Cyclic callback handler
17085181Sgd78059 */
17095181Sgd78059 static void
dmfe_cyclic(void * arg)17105181Sgd78059 dmfe_cyclic(void *arg)
17115181Sgd78059 {
17125181Sgd78059 dmfe_t *dmfep = arg; /* private device info */
17135181Sgd78059 uint32_t istat;
17145181Sgd78059 uint32_t gpsr;
17155181Sgd78059
17165181Sgd78059 /*
17175181Sgd78059 * If the chip's not RUNNING, there's nothing to do.
17185181Sgd78059 * If we can't get the mutex straight away, we'll just
17195181Sgd78059 * skip this pass; we'll back back soon enough anyway.
17205181Sgd78059 */
17215181Sgd78059 if (mutex_tryenter(dmfep->oplock) == 0)
17225181Sgd78059 return;
17239860Sgdamore@opensolaris.org if ((dmfep->suspended) || (dmfep->chip_state != CHIP_RUNNING)) {
17249860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
17259860Sgdamore@opensolaris.org return;
17269860Sgdamore@opensolaris.org }
17275181Sgd78059
17285181Sgd78059 /*
17295181Sgd78059 * Recheck chip state (it might have been stopped since we
17305181Sgd78059 * checked above). If still running, call each of the *tick*
17315181Sgd78059 * tasks. They will check for link change, TX stall, etc ...
17325181Sgd78059 */
17335181Sgd78059 if (dmfep->chip_state == CHIP_RUNNING) {
17345181Sgd78059 istat = dmfe_chip_get32(dmfep, STATUS_REG);
17355181Sgd78059 gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
17365181Sgd78059 dmfe_tick_stall_check(dmfep, gpsr, istat);
17375181Sgd78059 }
17385181Sgd78059
17395181Sgd78059 DRV_KS_INC(dmfep, KS_CYCLIC_RUN);
17405181Sgd78059 mutex_exit(dmfep->oplock);
17415181Sgd78059 }
17425181Sgd78059
17435181Sgd78059 /*
17445181Sgd78059 * ========== Hardware interrupt handler ==========
17455181Sgd78059 */
17465181Sgd78059
17475181Sgd78059 /*
17485181Sgd78059 * dmfe_interrupt() -- handle chip interrupts
17495181Sgd78059 */
17505181Sgd78059 static uint_t
dmfe_interrupt(caddr_t arg)17515181Sgd78059 dmfe_interrupt(caddr_t arg)
17525181Sgd78059 {
17535181Sgd78059 dmfe_t *dmfep; /* private device info */
17545181Sgd78059 uint32_t interrupts;
17555181Sgd78059 uint32_t istat;
17565181Sgd78059 const char *msg;
17575181Sgd78059 mblk_t *mp;
17585181Sgd78059 boolean_t warning_msg = B_TRUE;
17595181Sgd78059
17606990Sgd78059 dmfep = (void *)arg;
17615181Sgd78059
17629860Sgdamore@opensolaris.org mutex_enter(dmfep->oplock);
17639860Sgdamore@opensolaris.org if (dmfep->suspended) {
17649860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
17659860Sgdamore@opensolaris.org return (DDI_INTR_UNCLAIMED);
17669860Sgdamore@opensolaris.org }
17679860Sgdamore@opensolaris.org
17685181Sgd78059 /*
17695181Sgd78059 * A quick check as to whether the interrupt was from this
17705181Sgd78059 * device, before we even finish setting up all our local
17715181Sgd78059 * variables. Note that reading the interrupt status register
17725181Sgd78059 * doesn't have any unpleasant side effects such as clearing
17735181Sgd78059 * the bits read, so it's quite OK to re-read it once we have
17745181Sgd78059 * determined that we are going to service this interrupt and
17755181Sgd78059 * grabbed the mutexen.
17765181Sgd78059 */
17775181Sgd78059 istat = dmfe_chip_get32(dmfep, STATUS_REG);
17789860Sgdamore@opensolaris.org if ((istat & (NORMAL_SUMMARY_INT | ABNORMAL_SUMMARY_INT)) == 0) {
17799860Sgdamore@opensolaris.org
17809860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
17815181Sgd78059 return (DDI_INTR_UNCLAIMED);
17825181Sgd78059 }
17835181Sgd78059
17845181Sgd78059 DRV_KS_INC(dmfep, KS_INTERRUPT);
17855181Sgd78059
17865181Sgd78059 /*
17875181Sgd78059 * Identify bits that represent enabled interrupts ...
17885181Sgd78059 */
17895181Sgd78059 istat |= dmfe_chip_get32(dmfep, STATUS_REG);
17905181Sgd78059 interrupts = istat & dmfep->imask;
17915181Sgd78059 ASSERT(interrupts != 0);
17925181Sgd78059
17939860Sgdamore@opensolaris.org DTRACE_PROBE1(intr, uint32_t, istat);
17945181Sgd78059
17955181Sgd78059 /*
17965181Sgd78059 * Check for any interrupts other than TX/RX done.
17975181Sgd78059 * If there are any, they are considered Abnormal
17985181Sgd78059 * and will cause the chip to be reset.
17995181Sgd78059 */
18005181Sgd78059 if (interrupts & ~(RX_PKTDONE_INT | TX_PKTDONE_INT)) {
18015181Sgd78059 if (istat & ABNORMAL_SUMMARY_INT) {
18025181Sgd78059 /*
18035181Sgd78059 * Any Abnormal interrupts will lead to us
18045181Sgd78059 * resetting the chip, so we don't bother
18055181Sgd78059 * to clear each interrupt individually.
18065181Sgd78059 *
18075181Sgd78059 * Our main task here is to identify the problem,
18085181Sgd78059 * by pointing out the most significant unexpected
18095181Sgd78059 * bit. Additional bits may well be consequences
18105181Sgd78059 * of the first problem, so we consider the possible
18115181Sgd78059 * causes in order of severity.
18125181Sgd78059 */
18135181Sgd78059 if (interrupts & SYSTEM_ERR_INT) {
18145181Sgd78059 switch (istat & SYSTEM_ERR_BITS) {
18155181Sgd78059 case SYSTEM_ERR_M_ABORT:
18165181Sgd78059 msg = "Bus Master Abort";
18175181Sgd78059 break;
18185181Sgd78059
18195181Sgd78059 case SYSTEM_ERR_T_ABORT:
18205181Sgd78059 msg = "Bus Target Abort";
18215181Sgd78059 break;
18225181Sgd78059
18235181Sgd78059 case SYSTEM_ERR_PARITY:
18245181Sgd78059 msg = "Parity Error";
18255181Sgd78059 break;
18265181Sgd78059
18275181Sgd78059 default:
18285181Sgd78059 msg = "Unknown System Bus Error";
18295181Sgd78059 break;
18305181Sgd78059 }
18315181Sgd78059 } else if (interrupts & RX_STOPPED_INT) {
18325181Sgd78059 msg = "RX process stopped";
18335181Sgd78059 } else if (interrupts & RX_UNAVAIL_INT) {
18345181Sgd78059 msg = "RX buffer unavailable";
18355181Sgd78059 warning_msg = B_FALSE;
18365181Sgd78059 } else if (interrupts & RX_WATCHDOG_INT) {
18375181Sgd78059 msg = "RX watchdog timeout?";
18385181Sgd78059 } else if (interrupts & RX_EARLY_INT) {
18395181Sgd78059 msg = "RX early interrupt?";
18405181Sgd78059 } else if (interrupts & TX_STOPPED_INT) {
18415181Sgd78059 msg = "TX process stopped";
18425181Sgd78059 } else if (interrupts & TX_JABBER_INT) {
18435181Sgd78059 msg = "TX jabber timeout";
18445181Sgd78059 } else if (interrupts & TX_UNDERFLOW_INT) {
18455181Sgd78059 msg = "TX underflow?";
18465181Sgd78059 } else if (interrupts & TX_EARLY_INT) {
18475181Sgd78059 msg = "TX early interrupt?";
18485181Sgd78059
18495181Sgd78059 } else if (interrupts & LINK_STATUS_INT) {
18505181Sgd78059 msg = "Link status change?";
18515181Sgd78059 } else if (interrupts & GP_TIMER_INT) {
18525181Sgd78059 msg = "Timer expired?";
18535181Sgd78059 }
18545181Sgd78059
18555181Sgd78059 if (warning_msg)
18565181Sgd78059 dmfe_warning(dmfep, "abnormal interrupt, "
18575181Sgd78059 "status 0x%x: %s", istat, msg);
18585181Sgd78059
18595181Sgd78059 /*
18605181Sgd78059 * We don't want to run the entire reinitialisation
18615181Sgd78059 * code out of this (high-level?) interrupt, so we
18625181Sgd78059 * simply STOP the chip, and wake up the factotum
18635181Sgd78059 * to reinitalise it ...
18645181Sgd78059 */
18655181Sgd78059 dmfe_stop_chip(dmfep, CHIP_ERROR);
18665181Sgd78059 dmfe_wake_factotum(dmfep, KS_CHIP_ERROR,
18675181Sgd78059 "interrupt (error)");
18685181Sgd78059 } else {
18695181Sgd78059 /*
18705181Sgd78059 * We shouldn't really get here (it would mean
18715181Sgd78059 * there were some unprocessed enabled bits but
18725181Sgd78059 * they weren't Abnormal?), but we'll check just
18735181Sgd78059 * in case ...
18745181Sgd78059 */
18759860Sgdamore@opensolaris.org DTRACE_PROBE1(intr__unexpected, uint32_t, istat);
18765181Sgd78059 }
18775181Sgd78059 }
18785181Sgd78059
18795181Sgd78059 /*
18805181Sgd78059 * Acknowledge all the original bits - except in the case of an
18815181Sgd78059 * error, when we leave them unacknowledged so that the recovery
18825181Sgd78059 * code can see what was going on when the problem occurred ...
18835181Sgd78059 */
18845181Sgd78059 if (dmfep->chip_state != CHIP_ERROR) {
18855181Sgd78059 (void) dmfe_chip_put32(dmfep, STATUS_REG, istat);
18865181Sgd78059 /*
18875181Sgd78059 * Read-after-write forces completion on PCI bus.
18885181Sgd78059 *
18895181Sgd78059 */
18905181Sgd78059 (void) dmfe_chip_get32(dmfep, STATUS_REG);
18915181Sgd78059 }
18925181Sgd78059
18935181Sgd78059
18945181Sgd78059 /*
18955181Sgd78059 * We've finished talking to the chip, so we can drop <oplock>
18965181Sgd78059 * before handling the normal interrupts, which only involve
18975181Sgd78059 * manipulation of descriptors ...
18985181Sgd78059 */
18995181Sgd78059 mutex_exit(dmfep->oplock);
19005181Sgd78059
19015181Sgd78059 if (interrupts & RX_PKTDONE_INT)
19025181Sgd78059 if ((mp = dmfe_getp(dmfep)) != NULL)
19035181Sgd78059 mac_rx(dmfep->mh, NULL, mp);
19045181Sgd78059
19055181Sgd78059 if (interrupts & TX_PKTDONE_INT) {
19065181Sgd78059 /*
19075181Sgd78059 * The only reason for taking this interrupt is to give
19085181Sgd78059 * MAC a chance to schedule queued packets after a
19095181Sgd78059 * ring-full condition. To minimise the number of
19105181Sgd78059 * redundant TX-Done interrupts, we only mark two of the
19115181Sgd78059 * ring descriptors as 'interrupt-on-complete' - all the
19125181Sgd78059 * others are simply handed back without an interrupt.
19135181Sgd78059 */
19145181Sgd78059 if (dmfe_reclaim_on_done && mutex_tryenter(dmfep->txlock)) {
19155181Sgd78059 (void) dmfe_reclaim_tx_desc(dmfep);
19165181Sgd78059 mutex_exit(dmfep->txlock);
19175181Sgd78059 }
19185181Sgd78059 mac_tx_update(dmfep->mh);
19195181Sgd78059 }
19205181Sgd78059
19215181Sgd78059 return (DDI_INTR_CLAIMED);
19225181Sgd78059 }
19235181Sgd78059
19245181Sgd78059 /*
19255181Sgd78059 * ========== Statistics update handler ==========
19265181Sgd78059 */
19275181Sgd78059
19285181Sgd78059 static int
dmfe_m_stat(void * arg,uint_t stat,uint64_t * val)19295181Sgd78059 dmfe_m_stat(void *arg, uint_t stat, uint64_t *val)
19305181Sgd78059 {
19315181Sgd78059 dmfe_t *dmfep = arg;
19325181Sgd78059 int rv = 0;
19335181Sgd78059
19349860Sgdamore@opensolaris.org /* Let MII handle its own stats. */
19359860Sgdamore@opensolaris.org if (mii_m_getstat(dmfep->mii, stat, val) == 0) {
19369860Sgdamore@opensolaris.org return (0);
19379860Sgdamore@opensolaris.org }
19389860Sgdamore@opensolaris.org
19395181Sgd78059 mutex_enter(dmfep->oplock);
19405181Sgd78059 mutex_enter(dmfep->rxlock);
19415181Sgd78059 mutex_enter(dmfep->txlock);
19425181Sgd78059
19435181Sgd78059 /* make sure we have all the stats collected */
19445181Sgd78059 (void) dmfe_reclaim_tx_desc(dmfep);
19455181Sgd78059
19465181Sgd78059 switch (stat) {
19475181Sgd78059
19485181Sgd78059 case MAC_STAT_IPACKETS:
19495181Sgd78059 *val = dmfep->rx_stats_ipackets;
19505181Sgd78059 break;
19515181Sgd78059
19525181Sgd78059 case MAC_STAT_MULTIRCV:
19535181Sgd78059 *val = dmfep->rx_stats_multi;
19545181Sgd78059 break;
19555181Sgd78059
19565181Sgd78059 case MAC_STAT_BRDCSTRCV:
19575181Sgd78059 *val = dmfep->rx_stats_bcast;
19585181Sgd78059 break;
19595181Sgd78059
19605181Sgd78059 case MAC_STAT_RBYTES:
19615181Sgd78059 *val = dmfep->rx_stats_rbytes;
19625181Sgd78059 break;
19635181Sgd78059
19645181Sgd78059 case MAC_STAT_IERRORS:
19655181Sgd78059 *val = dmfep->rx_stats_ierrors;
19665181Sgd78059 break;
19675181Sgd78059
19685181Sgd78059 case MAC_STAT_NORCVBUF:
19695181Sgd78059 *val = dmfep->rx_stats_norcvbuf;
19705181Sgd78059 break;
19715181Sgd78059
19725181Sgd78059 case MAC_STAT_COLLISIONS:
19735181Sgd78059 *val = dmfep->tx_stats_collisions;
19745181Sgd78059 break;
19755181Sgd78059
19765181Sgd78059 case MAC_STAT_OERRORS:
19775181Sgd78059 *val = dmfep->tx_stats_oerrors;
19785181Sgd78059 break;
19795181Sgd78059
19805181Sgd78059 case MAC_STAT_OPACKETS:
19815181Sgd78059 *val = dmfep->tx_stats_opackets;
19825181Sgd78059 break;
19835181Sgd78059
19845181Sgd78059 case MAC_STAT_MULTIXMT:
19855181Sgd78059 *val = dmfep->tx_stats_multi;
19865181Sgd78059 break;
19875181Sgd78059
19885181Sgd78059 case MAC_STAT_BRDCSTXMT:
19895181Sgd78059 *val = dmfep->tx_stats_bcast;
19905181Sgd78059 break;
19915181Sgd78059
19925181Sgd78059 case MAC_STAT_OBYTES:
19935181Sgd78059 *val = dmfep->tx_stats_obytes;
19945181Sgd78059 break;
19955181Sgd78059
19965181Sgd78059 case MAC_STAT_OVERFLOWS:
19975181Sgd78059 *val = dmfep->rx_stats_overflow;
19985181Sgd78059 break;
19995181Sgd78059
20005181Sgd78059 case MAC_STAT_UNDERFLOWS:
20015181Sgd78059 *val = dmfep->tx_stats_underflow;
20025181Sgd78059 break;
20035181Sgd78059
20045181Sgd78059 case ETHER_STAT_ALIGN_ERRORS:
20055181Sgd78059 *val = dmfep->rx_stats_align;
20065181Sgd78059 break;
20075181Sgd78059
20085181Sgd78059 case ETHER_STAT_FCS_ERRORS:
20095181Sgd78059 *val = dmfep->rx_stats_fcs;
20105181Sgd78059 break;
20115181Sgd78059
20125181Sgd78059 case ETHER_STAT_TOOLONG_ERRORS:
20135181Sgd78059 *val = dmfep->rx_stats_toolong;
20145181Sgd78059 break;
20155181Sgd78059
20165181Sgd78059 case ETHER_STAT_TOOSHORT_ERRORS:
20175181Sgd78059 *val = dmfep->rx_stats_short;
20185181Sgd78059 break;
20195181Sgd78059
20205181Sgd78059 case ETHER_STAT_MACRCV_ERRORS:
20215181Sgd78059 *val = dmfep->rx_stats_macrcv_errors;
20225181Sgd78059 break;
20235181Sgd78059
20245181Sgd78059 case ETHER_STAT_MACXMT_ERRORS:
20255181Sgd78059 *val = dmfep->tx_stats_macxmt_errors;
20265181Sgd78059 break;
20275181Sgd78059
20285181Sgd78059 case ETHER_STAT_JABBER_ERRORS:
20295181Sgd78059 *val = dmfep->tx_stats_jabber;
20305181Sgd78059 break;
20315181Sgd78059
20325181Sgd78059 case ETHER_STAT_CARRIER_ERRORS:
20335181Sgd78059 *val = dmfep->tx_stats_nocarrier;
20345181Sgd78059 break;
20355181Sgd78059
20365181Sgd78059 case ETHER_STAT_TX_LATE_COLLISIONS:
20375181Sgd78059 *val = dmfep->tx_stats_xmtlatecoll;
20385181Sgd78059 break;
20395181Sgd78059
20405181Sgd78059 case ETHER_STAT_EX_COLLISIONS:
20415181Sgd78059 *val = dmfep->tx_stats_excoll;
20425181Sgd78059 break;
20435181Sgd78059
20445181Sgd78059 case ETHER_STAT_DEFER_XMTS:
20455181Sgd78059 *val = dmfep->tx_stats_defer;
20465181Sgd78059 break;
20475181Sgd78059
20485181Sgd78059 case ETHER_STAT_FIRST_COLLISIONS:
20495181Sgd78059 *val = dmfep->tx_stats_first_coll;
20505181Sgd78059 break;
20515181Sgd78059
20525181Sgd78059 case ETHER_STAT_MULTI_COLLISIONS:
20535181Sgd78059 *val = dmfep->tx_stats_multi_coll;
20545181Sgd78059 break;
20555181Sgd78059
20565181Sgd78059 default:
20575181Sgd78059 rv = ENOTSUP;
20585181Sgd78059 }
20595181Sgd78059
20605181Sgd78059 mutex_exit(dmfep->txlock);
20615181Sgd78059 mutex_exit(dmfep->rxlock);
20625181Sgd78059 mutex_exit(dmfep->oplock);
20635181Sgd78059
20645181Sgd78059 return (rv);
20655181Sgd78059 }
20665181Sgd78059
20675181Sgd78059 /*
20685181Sgd78059 * ========== Ioctl handler & subfunctions ==========
20695181Sgd78059 */
20705181Sgd78059
20719860Sgdamore@opensolaris.org static lb_property_t dmfe_loopmodes[] = {
20729860Sgdamore@opensolaris.org { normal, "normal", 0 },
20739860Sgdamore@opensolaris.org { internal, "Internal", 1 },
20749860Sgdamore@opensolaris.org { external, "External", 2 },
20759860Sgdamore@opensolaris.org };
20765181Sgd78059
20775181Sgd78059 /*
20785181Sgd78059 * Specific dmfe IOCTLs, the mac module handles the generic ones.
20799860Sgdamore@opensolaris.org * Unfortunately, the DM9102 doesn't seem to work well with MII based
20809860Sgdamore@opensolaris.org * loopback, so we have to do something special for it.
20815181Sgd78059 */
20829860Sgdamore@opensolaris.org
20835181Sgd78059 static void
dmfe_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)20845181Sgd78059 dmfe_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
20855181Sgd78059 {
20869860Sgdamore@opensolaris.org dmfe_t *dmfep = arg;
20879860Sgdamore@opensolaris.org struct iocblk *iocp;
20889860Sgdamore@opensolaris.org int rv = 0;
20899860Sgdamore@opensolaris.org lb_info_sz_t sz;
20909860Sgdamore@opensolaris.org int cmd;
20919860Sgdamore@opensolaris.org uint32_t mode;
20929860Sgdamore@opensolaris.org
20936990Sgd78059 iocp = (void *)mp->b_rptr;
20945181Sgd78059 cmd = iocp->ioc_cmd;
20959860Sgdamore@opensolaris.org
20969860Sgdamore@opensolaris.org if (mp->b_cont == NULL) {
20979860Sgdamore@opensolaris.org /*
20989860Sgdamore@opensolaris.org * All of these ioctls need data!
20999860Sgdamore@opensolaris.org */
21005181Sgd78059 miocnak(wq, mp, 0, EINVAL);
21015181Sgd78059 return;
21025181Sgd78059 }
21035181Sgd78059
21045181Sgd78059 switch (cmd) {
21059860Sgdamore@opensolaris.org case LB_GET_INFO_SIZE:
21069860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (sz)) {
21079860Sgdamore@opensolaris.org rv = EINVAL;
21089860Sgdamore@opensolaris.org } else {
21099860Sgdamore@opensolaris.org sz = sizeof (dmfe_loopmodes);
21109860Sgdamore@opensolaris.org bcopy(&sz, mp->b_cont->b_rptr, sizeof (sz));
21119860Sgdamore@opensolaris.org }
21129860Sgdamore@opensolaris.org break;
21139860Sgdamore@opensolaris.org
21149860Sgdamore@opensolaris.org case LB_GET_INFO:
21159860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (dmfe_loopmodes)) {
21169860Sgdamore@opensolaris.org rv = EINVAL;
21179860Sgdamore@opensolaris.org } else {
21189860Sgdamore@opensolaris.org bcopy(dmfe_loopmodes, mp->b_cont->b_rptr,
21199860Sgdamore@opensolaris.org iocp->ioc_count);
21209860Sgdamore@opensolaris.org }
21215181Sgd78059 break;
21225181Sgd78059
21239860Sgdamore@opensolaris.org case LB_GET_MODE:
21249860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) {
21259860Sgdamore@opensolaris.org rv = EINVAL;
21269860Sgdamore@opensolaris.org } else {
21279860Sgdamore@opensolaris.org mutex_enter(dmfep->oplock);
21289860Sgdamore@opensolaris.org switch (dmfep->opmode & LOOPBACK_MODE_MASK) {
21299860Sgdamore@opensolaris.org case LOOPBACK_OFF:
21309860Sgdamore@opensolaris.org mode = 0;
21319860Sgdamore@opensolaris.org break;
21329860Sgdamore@opensolaris.org case LOOPBACK_INTERNAL:
21339860Sgdamore@opensolaris.org mode = 1;
21349860Sgdamore@opensolaris.org break;
21359860Sgdamore@opensolaris.org default:
21369860Sgdamore@opensolaris.org mode = 2;
21379860Sgdamore@opensolaris.org break;
21389860Sgdamore@opensolaris.org }
21399860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
21409860Sgdamore@opensolaris.org bcopy(&mode, mp->b_cont->b_rptr, sizeof (mode));
21419860Sgdamore@opensolaris.org }
21425181Sgd78059 break;
21435181Sgd78059
21449860Sgdamore@opensolaris.org case LB_SET_MODE:
21459860Sgdamore@opensolaris.org rv = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
21469860Sgdamore@opensolaris.org if (rv != 0)
21479860Sgdamore@opensolaris.org break;
21489860Sgdamore@opensolaris.org if (iocp->ioc_count != sizeof (mode)) {
21499860Sgdamore@opensolaris.org rv = EINVAL;
21509860Sgdamore@opensolaris.org break;
21519860Sgdamore@opensolaris.org }
21529860Sgdamore@opensolaris.org bcopy(mp->b_cont->b_rptr, &mode, sizeof (mode));
21539860Sgdamore@opensolaris.org
21549860Sgdamore@opensolaris.org mutex_enter(dmfep->oplock);
21559860Sgdamore@opensolaris.org dmfep->opmode &= ~LOOPBACK_MODE_MASK;
21569860Sgdamore@opensolaris.org switch (mode) {
21579860Sgdamore@opensolaris.org case 2:
21589860Sgdamore@opensolaris.org dmfep->opmode |= LOOPBACK_PHY_D;
21599860Sgdamore@opensolaris.org break;
21609860Sgdamore@opensolaris.org case 1:
21619860Sgdamore@opensolaris.org dmfep->opmode |= LOOPBACK_INTERNAL;
21629860Sgdamore@opensolaris.org break;
21639860Sgdamore@opensolaris.org default:
21649860Sgdamore@opensolaris.org break;
21659860Sgdamore@opensolaris.org }
21669860Sgdamore@opensolaris.org if (!dmfep->suspended) {
21679860Sgdamore@opensolaris.org dmfe_restart(dmfep);
21689860Sgdamore@opensolaris.org }
21699860Sgdamore@opensolaris.org mutex_exit(dmfep->oplock);
21709860Sgdamore@opensolaris.org break;
21719860Sgdamore@opensolaris.org
21729860Sgdamore@opensolaris.org default:
21739860Sgdamore@opensolaris.org rv = EINVAL;
21745181Sgd78059 break;
21755181Sgd78059 }
21765181Sgd78059
21779860Sgdamore@opensolaris.org if (rv == 0) {
21789860Sgdamore@opensolaris.org miocack(wq, mp, iocp->ioc_count, 0);
21799860Sgdamore@opensolaris.org } else {
21809860Sgdamore@opensolaris.org miocnak(wq, mp, 0, rv);
21815181Sgd78059 }
21825181Sgd78059 }
21835181Sgd78059
21849860Sgdamore@opensolaris.org int
dmfe_m_getprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,void * val)2185*11878SVenu.Iyer@Sun.COM dmfe_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
2186*11878SVenu.Iyer@Sun.COM void *val)
21879860Sgdamore@opensolaris.org {
21889860Sgdamore@opensolaris.org dmfe_t *dmfep = arg;
21899860Sgdamore@opensolaris.org
2190*11878SVenu.Iyer@Sun.COM return (mii_m_getprop(dmfep->mii, name, num, sz, val));
21919860Sgdamore@opensolaris.org }
21929860Sgdamore@opensolaris.org
21939860Sgdamore@opensolaris.org int
dmfe_m_setprop(void * arg,const char * name,mac_prop_id_t num,uint_t sz,const void * val)21949860Sgdamore@opensolaris.org dmfe_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
21959860Sgdamore@opensolaris.org const void *val)
21969860Sgdamore@opensolaris.org {
21979860Sgdamore@opensolaris.org dmfe_t *dmfep = arg;
21989860Sgdamore@opensolaris.org
21999860Sgdamore@opensolaris.org return (mii_m_setprop(dmfep->mii, name, num, sz, val));
22009860Sgdamore@opensolaris.org }
22015181Sgd78059
2202*11878SVenu.Iyer@Sun.COM static void
dmfe_m_propinfo(void * arg,const char * name,mac_prop_id_t num,mac_prop_info_handle_t mph)2203*11878SVenu.Iyer@Sun.COM dmfe_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
2204*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t mph)
2205*11878SVenu.Iyer@Sun.COM {
2206*11878SVenu.Iyer@Sun.COM dmfe_t *dmfep = arg;
2207*11878SVenu.Iyer@Sun.COM
2208*11878SVenu.Iyer@Sun.COM mii_m_propinfo(dmfep->mii, name, num, mph);
2209*11878SVenu.Iyer@Sun.COM }
22105181Sgd78059
22115181Sgd78059 /*
22125181Sgd78059 * ========== Per-instance setup/teardown code ==========
22135181Sgd78059 */
22145181Sgd78059
22155181Sgd78059 /*
22165181Sgd78059 * Determine local MAC address & broadcast address for this interface
22175181Sgd78059 */
22185181Sgd78059 static void
dmfe_find_mac_address(dmfe_t * dmfep)22195181Sgd78059 dmfe_find_mac_address(dmfe_t *dmfep)
22205181Sgd78059 {
22215181Sgd78059 uchar_t *prop;
22225181Sgd78059 uint_t propsize;
22235181Sgd78059 int err;
22245181Sgd78059
22255181Sgd78059 /*
22265181Sgd78059 * We have to find the "vendor's factory-set address". This is
22275181Sgd78059 * the value of the property "local-mac-address", as set by OBP
22285181Sgd78059 * (or a .conf file!)
22295181Sgd78059 *
22305181Sgd78059 * If the property is not there, then we try to find the factory
22315181Sgd78059 * mac address from the devices serial EEPROM.
22325181Sgd78059 */
22335181Sgd78059 bzero(dmfep->curr_addr, sizeof (dmfep->curr_addr));
22345181Sgd78059 err = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dmfep->devinfo,
22355181Sgd78059 DDI_PROP_DONTPASS, localmac_propname, &prop, &propsize);
22365181Sgd78059 if (err == DDI_PROP_SUCCESS) {
22375181Sgd78059 if (propsize == ETHERADDRL)
22385181Sgd78059 ethaddr_copy(prop, dmfep->curr_addr);
22395181Sgd78059 ddi_prop_free(prop);
22405181Sgd78059 } else {
22415181Sgd78059 /* no property set... check eeprom */
22425181Sgd78059 dmfe_read_eeprom(dmfep, EEPROM_EN_ADDR, dmfep->curr_addr,
22435181Sgd78059 ETHERADDRL);
22445181Sgd78059 }
22455181Sgd78059 }
22465181Sgd78059
22475181Sgd78059 static int
dmfe_alloc_dma_mem(dmfe_t * dmfep,size_t memsize,size_t setup,size_t slop,ddi_device_acc_attr_t * attr_p,uint_t dma_flags,dma_area_t * dma_p)22485181Sgd78059 dmfe_alloc_dma_mem(dmfe_t *dmfep, size_t memsize,
22495181Sgd78059 size_t setup, size_t slop, ddi_device_acc_attr_t *attr_p,
22505181Sgd78059 uint_t dma_flags, dma_area_t *dma_p)
22515181Sgd78059 {
22525181Sgd78059 ddi_dma_cookie_t dma_cookie;
22535181Sgd78059 uint_t ncookies;
22545181Sgd78059 int err;
22555181Sgd78059
22565181Sgd78059 /*
22575181Sgd78059 * Allocate handle
22585181Sgd78059 */
22595181Sgd78059 err = ddi_dma_alloc_handle(dmfep->devinfo, &dma_attr,
22605181Sgd78059 DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
22619860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
22629860Sgdamore@opensolaris.org dmfe_error(dmfep, "DMA handle allocation failed");
22635181Sgd78059 return (DDI_FAILURE);
22649860Sgdamore@opensolaris.org }
22655181Sgd78059
22665181Sgd78059 /*
22675181Sgd78059 * Allocate memory
22685181Sgd78059 */
22695181Sgd78059 err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize + setup + slop,
22705181Sgd78059 attr_p, dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING),
22715181Sgd78059 DDI_DMA_SLEEP, NULL,
22725181Sgd78059 &dma_p->mem_va, &dma_p->alength, &dma_p->acc_hdl);
22739860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
22749860Sgdamore@opensolaris.org dmfe_error(dmfep, "DMA memory allocation failed: %d", err);
22755181Sgd78059 return (DDI_FAILURE);
22769860Sgdamore@opensolaris.org }
22775181Sgd78059
22785181Sgd78059 /*
22795181Sgd78059 * Bind the two together
22805181Sgd78059 */
22815181Sgd78059 err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
22825181Sgd78059 dma_p->mem_va, dma_p->alength, dma_flags,
22835181Sgd78059 DDI_DMA_SLEEP, NULL, &dma_cookie, &ncookies);
22849860Sgdamore@opensolaris.org if (err != DDI_DMA_MAPPED) {
22859860Sgdamore@opensolaris.org dmfe_error(dmfep, "DMA mapping failed: %d", err);
22865181Sgd78059 return (DDI_FAILURE);
22879860Sgdamore@opensolaris.org }
22889860Sgdamore@opensolaris.org if ((dma_p->ncookies = ncookies) != 1) {
22899860Sgdamore@opensolaris.org dmfe_error(dmfep, "Too many DMA cookeis: %d", ncookies);
22905181Sgd78059 return (DDI_FAILURE);
22919860Sgdamore@opensolaris.org }
22925181Sgd78059
22935181Sgd78059 dma_p->mem_dvma = dma_cookie.dmac_address;
22945181Sgd78059 if (setup > 0) {
22955181Sgd78059 dma_p->setup_dvma = dma_p->mem_dvma + memsize;
22965181Sgd78059 dma_p->setup_va = dma_p->mem_va + memsize;
22975181Sgd78059 } else {
22985181Sgd78059 dma_p->setup_dvma = 0;
22995181Sgd78059 dma_p->setup_va = NULL;
23005181Sgd78059 }
23015181Sgd78059
23025181Sgd78059 return (DDI_SUCCESS);
23035181Sgd78059 }
23045181Sgd78059
23055181Sgd78059 /*
23065181Sgd78059 * This function allocates the transmit and receive buffers and descriptors.
23075181Sgd78059 */
23085181Sgd78059 static int
dmfe_alloc_bufs(dmfe_t * dmfep)23095181Sgd78059 dmfe_alloc_bufs(dmfe_t *dmfep)
23105181Sgd78059 {
23115181Sgd78059 size_t memsize;
23125181Sgd78059 int err;
23135181Sgd78059
23145181Sgd78059 /*
23155181Sgd78059 * Allocate memory & handles for TX descriptor ring
23165181Sgd78059 */
23175181Sgd78059 memsize = dmfep->tx.n_desc * sizeof (struct tx_desc_type);
23185181Sgd78059 err = dmfe_alloc_dma_mem(dmfep, memsize, SETUPBUF_SIZE, DMFE_SLOP,
23195181Sgd78059 &dmfe_reg_accattr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
23205181Sgd78059 &dmfep->tx_desc);
23219860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
23229860Sgdamore@opensolaris.org dmfe_error(dmfep, "TX descriptor allocation failed");
23235181Sgd78059 return (DDI_FAILURE);
23249860Sgdamore@opensolaris.org }
23255181Sgd78059
23265181Sgd78059 /*
23275181Sgd78059 * Allocate memory & handles for TX buffers
23285181Sgd78059 */
23295181Sgd78059 memsize = dmfep->tx.n_desc * DMFE_BUF_SIZE;
23305181Sgd78059 err = dmfe_alloc_dma_mem(dmfep, memsize, 0, 0,
23315181Sgd78059 &dmfe_data_accattr, DDI_DMA_WRITE | DMFE_DMA_MODE,
23325181Sgd78059 &dmfep->tx_buff);
23339860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
23349860Sgdamore@opensolaris.org dmfe_error(dmfep, "TX buffer allocation failed");
23355181Sgd78059 return (DDI_FAILURE);
23369860Sgdamore@opensolaris.org }
23375181Sgd78059
23385181Sgd78059 /*
23395181Sgd78059 * Allocate memory & handles for RX descriptor ring
23405181Sgd78059 */
23415181Sgd78059 memsize = dmfep->rx.n_desc * sizeof (struct rx_desc_type);
23425181Sgd78059 err = dmfe_alloc_dma_mem(dmfep, memsize, 0, DMFE_SLOP,
23435181Sgd78059 &dmfe_reg_accattr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
23445181Sgd78059 &dmfep->rx_desc);
23459860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
23469860Sgdamore@opensolaris.org dmfe_error(dmfep, "RX descriptor allocation failed");
23475181Sgd78059 return (DDI_FAILURE);
23489860Sgdamore@opensolaris.org }
23495181Sgd78059
23505181Sgd78059 /*
23515181Sgd78059 * Allocate memory & handles for RX buffers
23525181Sgd78059 */
23535181Sgd78059 memsize = dmfep->rx.n_desc * DMFE_BUF_SIZE;
23545181Sgd78059 err = dmfe_alloc_dma_mem(dmfep, memsize, 0, 0,
23555181Sgd78059 &dmfe_data_accattr, DDI_DMA_READ | DMFE_DMA_MODE, &dmfep->rx_buff);
23569860Sgdamore@opensolaris.org if (err != DDI_SUCCESS) {
23579860Sgdamore@opensolaris.org dmfe_error(dmfep, "RX buffer allocation failed");
23585181Sgd78059 return (DDI_FAILURE);
23599860Sgdamore@opensolaris.org }
23605181Sgd78059
23615181Sgd78059 /*
23625181Sgd78059 * Allocate bitmasks for tx packet type tracking
23635181Sgd78059 */
23645181Sgd78059 dmfep->tx_mcast = kmem_zalloc(dmfep->tx.n_desc / NBBY, KM_SLEEP);
23655181Sgd78059 dmfep->tx_bcast = kmem_zalloc(dmfep->tx.n_desc / NBBY, KM_SLEEP);
23665181Sgd78059
23675181Sgd78059 return (DDI_SUCCESS);
23685181Sgd78059 }
23695181Sgd78059
23705181Sgd78059 static void
dmfe_free_dma_mem(dma_area_t * dma_p)23715181Sgd78059 dmfe_free_dma_mem(dma_area_t *dma_p)
23725181Sgd78059 {
23735181Sgd78059 if (dma_p->dma_hdl != NULL) {
23745181Sgd78059 if (dma_p->ncookies) {
23755181Sgd78059 (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
23765181Sgd78059 dma_p->ncookies = 0;
23775181Sgd78059 }
23785181Sgd78059 ddi_dma_free_handle(&dma_p->dma_hdl);
23795181Sgd78059 dma_p->dma_hdl = NULL;
23805181Sgd78059 dma_p->mem_dvma = 0;
23815181Sgd78059 dma_p->setup_dvma = 0;
23825181Sgd78059 }
23835181Sgd78059
23845181Sgd78059 if (dma_p->acc_hdl != NULL) {
23855181Sgd78059 ddi_dma_mem_free(&dma_p->acc_hdl);
23865181Sgd78059 dma_p->acc_hdl = NULL;
23875181Sgd78059 dma_p->mem_va = NULL;
23885181Sgd78059 dma_p->setup_va = NULL;
23895181Sgd78059 }
23905181Sgd78059 }
23915181Sgd78059
23925181Sgd78059 /*
23935181Sgd78059 * This routine frees the transmit and receive buffers and descriptors.
23945181Sgd78059 * Make sure the chip is stopped before calling it!
23955181Sgd78059 */
23965181Sgd78059 static void
dmfe_free_bufs(dmfe_t * dmfep)23975181Sgd78059 dmfe_free_bufs(dmfe_t *dmfep)
23985181Sgd78059 {
23995181Sgd78059 dmfe_free_dma_mem(&dmfep->rx_buff);
24005181Sgd78059 dmfe_free_dma_mem(&dmfep->rx_desc);
24015181Sgd78059 dmfe_free_dma_mem(&dmfep->tx_buff);
24025181Sgd78059 dmfe_free_dma_mem(&dmfep->tx_desc);
24039860Sgdamore@opensolaris.org if (dmfep->tx_mcast)
24049860Sgdamore@opensolaris.org kmem_free(dmfep->tx_mcast, dmfep->tx.n_desc / NBBY);
24059860Sgdamore@opensolaris.org if (dmfep->tx_bcast)
24069860Sgdamore@opensolaris.org kmem_free(dmfep->tx_bcast, dmfep->tx.n_desc / NBBY);
24075181Sgd78059 }
24085181Sgd78059
24095181Sgd78059 static void
dmfe_unattach(dmfe_t * dmfep)24105181Sgd78059 dmfe_unattach(dmfe_t *dmfep)
24115181Sgd78059 {
24125181Sgd78059 /*
24135181Sgd78059 * Clean up and free all DMFE data structures
24145181Sgd78059 */
24155181Sgd78059 if (dmfep->cycid != NULL) {
24165181Sgd78059 ddi_periodic_delete(dmfep->cycid);
24175181Sgd78059 dmfep->cycid = NULL;
24185181Sgd78059 }
24195181Sgd78059
24205181Sgd78059 if (dmfep->ksp_drv != NULL)
24215181Sgd78059 kstat_delete(dmfep->ksp_drv);
24225181Sgd78059 if (dmfep->progress & PROGRESS_HWINT) {
24235181Sgd78059 ddi_remove_intr(dmfep->devinfo, 0, dmfep->iblk);
24249860Sgdamore@opensolaris.org }
24259860Sgdamore@opensolaris.org if (dmfep->progress & PROGRESS_SOFTINT)
24269860Sgdamore@opensolaris.org ddi_remove_softintr(dmfep->factotum_id);
24279860Sgdamore@opensolaris.org if (dmfep->mii != NULL)
24289860Sgdamore@opensolaris.org mii_free(dmfep->mii);
24299860Sgdamore@opensolaris.org if (dmfep->progress & PROGRESS_MUTEX) {
24305181Sgd78059 mutex_destroy(dmfep->txlock);
24315181Sgd78059 mutex_destroy(dmfep->rxlock);
24325181Sgd78059 mutex_destroy(dmfep->oplock);
24335181Sgd78059 }
24349860Sgdamore@opensolaris.org dmfe_free_bufs(dmfep);
24359860Sgdamore@opensolaris.org if (dmfep->io_handle != NULL)
24365181Sgd78059 ddi_regs_map_free(&dmfep->io_handle);
24375181Sgd78059
24385181Sgd78059 kmem_free(dmfep, sizeof (*dmfep));
24395181Sgd78059 }
24405181Sgd78059
24415181Sgd78059 static int
dmfe_config_init(dmfe_t * dmfep,chip_id_t * idp)24425181Sgd78059 dmfe_config_init(dmfe_t *dmfep, chip_id_t *idp)
24435181Sgd78059 {
24445181Sgd78059 ddi_acc_handle_t handle;
24455181Sgd78059 uint32_t regval;
24465181Sgd78059
24475181Sgd78059 if (pci_config_setup(dmfep->devinfo, &handle) != DDI_SUCCESS)
24485181Sgd78059 return (DDI_FAILURE);
24495181Sgd78059
24505181Sgd78059 /*
24515181Sgd78059 * Get vendor/device/revision. We expect (but don't check) that
24525181Sgd78059 * (vendorid == DAVICOM_VENDOR_ID) && (deviceid == DEVICE_ID_9102)
24535181Sgd78059 */
24545181Sgd78059 idp->vendor = pci_config_get16(handle, PCI_CONF_VENID);
24555181Sgd78059 idp->device = pci_config_get16(handle, PCI_CONF_DEVID);
24565181Sgd78059 idp->revision = pci_config_get8(handle, PCI_CONF_REVID);
24575181Sgd78059
24585181Sgd78059 /*
24595181Sgd78059 * Turn on Bus Master Enable bit and ensure the device is not asleep
24605181Sgd78059 */
24615181Sgd78059 regval = pci_config_get32(handle, PCI_CONF_COMM);
24625181Sgd78059 pci_config_put32(handle, PCI_CONF_COMM, (regval | PCI_COMM_ME));
24635181Sgd78059
24645181Sgd78059 regval = pci_config_get32(handle, PCI_DMFE_CONF_CFDD);
24655181Sgd78059 pci_config_put32(handle, PCI_DMFE_CONF_CFDD,
24665181Sgd78059 regval & ~(CFDD_SLEEP | CFDD_SNOOZE));
24675181Sgd78059
24685181Sgd78059 pci_config_teardown(&handle);
24695181Sgd78059 return (DDI_SUCCESS);
24705181Sgd78059 }
24715181Sgd78059
24725181Sgd78059 struct ks_index {
24735181Sgd78059 int index;
24745181Sgd78059 char *name;
24755181Sgd78059 };
24765181Sgd78059
24775181Sgd78059 static const struct ks_index ks_drv_names[] = {
24785181Sgd78059 { KS_INTERRUPT, "intr" },
24795181Sgd78059 { KS_CYCLIC_RUN, "cyclic_run" },
24805181Sgd78059
24815181Sgd78059 { KS_TX_STALL, "tx_stall_detect" },
24825181Sgd78059 { KS_CHIP_ERROR, "chip_error_interrupt" },
24835181Sgd78059
24845181Sgd78059 { KS_FACTOTUM_RUN, "factotum_run" },
24855181Sgd78059 { KS_RECOVERY, "factotum_recover" },
24865181Sgd78059
24875181Sgd78059 { -1, NULL }
24885181Sgd78059 };
24895181Sgd78059
24905181Sgd78059 static void
dmfe_init_kstats(dmfe_t * dmfep,int instance)24915181Sgd78059 dmfe_init_kstats(dmfe_t *dmfep, int instance)
24925181Sgd78059 {
24935181Sgd78059 kstat_t *ksp;
24945181Sgd78059 kstat_named_t *knp;
24955181Sgd78059 const struct ks_index *ksip;
24965181Sgd78059
24975181Sgd78059 /* no need to create MII stats, the mac module already does it */
24985181Sgd78059
24995181Sgd78059 /* Create and initialise driver-defined kstats */
25005181Sgd78059 ksp = kstat_create(DRIVER_NAME, instance, "dmfe_events", "net",
25015181Sgd78059 KSTAT_TYPE_NAMED, KS_DRV_COUNT, KSTAT_FLAG_PERSISTENT);
25025181Sgd78059 if (ksp != NULL) {
25035181Sgd78059 for (knp = ksp->ks_data, ksip = ks_drv_names;
25045181Sgd78059 ksip->name != NULL; ++ksip) {
25055181Sgd78059 kstat_named_init(&knp[ksip->index], ksip->name,
25065181Sgd78059 KSTAT_DATA_UINT64);
25075181Sgd78059 }
25085181Sgd78059 dmfep->ksp_drv = ksp;
25095181Sgd78059 dmfep->knp_drv = knp;
25105181Sgd78059 kstat_install(ksp);
25115181Sgd78059 } else {
25125181Sgd78059 dmfe_error(dmfep, "kstat_create() for dmfe_events failed");
25135181Sgd78059 }
25145181Sgd78059 }
25155181Sgd78059
25165181Sgd78059 static int
dmfe_resume(dev_info_t * devinfo)25175181Sgd78059 dmfe_resume(dev_info_t *devinfo)
25185181Sgd78059 {
25195181Sgd78059 dmfe_t *dmfep; /* Our private data */
25205181Sgd78059 chip_id_t chipid;
25219860Sgdamore@opensolaris.org boolean_t restart = B_FALSE;
25225181Sgd78059
25235181Sgd78059 dmfep = ddi_get_driver_private(devinfo);
25245181Sgd78059 if (dmfep == NULL)
25255181Sgd78059 return (DDI_FAILURE);
25265181Sgd78059
25275181Sgd78059 /*
25285181Sgd78059 * Refuse to resume if the data structures aren't consistent
25295181Sgd78059 */
25305181Sgd78059 if (dmfep->devinfo != devinfo)
25315181Sgd78059 return (DDI_FAILURE);
25325181Sgd78059
25335181Sgd78059 /*
25345181Sgd78059 * Refuse to resume if the chip's changed its identity (*boggle*)
25355181Sgd78059 */
25365181Sgd78059 if (dmfe_config_init(dmfep, &chipid) != DDI_SUCCESS)
25375181Sgd78059 return (DDI_FAILURE);
25385181Sgd78059 if (chipid.vendor != dmfep->chipid.vendor)
25395181Sgd78059 return (DDI_FAILURE);
25405181Sgd78059 if (chipid.device != dmfep->chipid.device)
25415181Sgd78059 return (DDI_FAILURE);
25425181Sgd78059 if (chipid.revision != dmfep->chipid.revision)
25435181Sgd78059 return (DDI_FAILURE);
25445181Sgd78059
25459860Sgdamore@opensolaris.org mutex_enter(dmfep->oplock);
25469860Sgdamore@opensolaris.org mutex_enter(dmfep->txlock);
25479860Sgdamore@opensolaris.org dmfep->suspended = B_FALSE;
25489860Sgdamore@opensolaris.org mutex_exit(dmfep->txlock);
25499860Sgdamore@opensolaris.org
25505181Sgd78059 /*
25515181Sgd78059 * All OK, reinitialise h/w & kick off MAC scheduling
25525181Sgd78059 */
25539860Sgdamore@opensolaris.org if (dmfep->mac_state == DMFE_MAC_STARTED) {
25549860Sgdamore@opensolaris.org dmfe_restart(dmfep);
25559860Sgdamore@opensolaris.org restart = B_TRUE;
25569860Sgdamore@opensolaris.org }
25575181Sgd78059 mutex_exit(dmfep->oplock);
25589860Sgdamore@opensolaris.org
25599860Sgdamore@opensolaris.org if (restart) {
25609860Sgdamore@opensolaris.org mii_resume(dmfep->mii);
25619860Sgdamore@opensolaris.org mac_tx_update(dmfep->mh);
25629860Sgdamore@opensolaris.org }
25635181Sgd78059 return (DDI_SUCCESS);
25645181Sgd78059 }
25655181Sgd78059
25665181Sgd78059 /*
25675181Sgd78059 * attach(9E) -- Attach a device to the system
25685181Sgd78059 *
25695181Sgd78059 * Called once for each board successfully probed.
25705181Sgd78059 */
25715181Sgd78059 static int
dmfe_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)25725181Sgd78059 dmfe_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
25735181Sgd78059 {
25745181Sgd78059 mac_register_t *macp;
25755181Sgd78059 dmfe_t *dmfep; /* Our private data */
25765181Sgd78059 uint32_t csr6;
25775181Sgd78059 int instance;
25785181Sgd78059 int err;
25795181Sgd78059
25805181Sgd78059 instance = ddi_get_instance(devinfo);
25815181Sgd78059
25825181Sgd78059 switch (cmd) {
25835181Sgd78059 default:
25845181Sgd78059 return (DDI_FAILURE);
25855181Sgd78059
25865181Sgd78059 case DDI_RESUME:
25875181Sgd78059 return (dmfe_resume(devinfo));
25885181Sgd78059
25895181Sgd78059 case DDI_ATTACH:
25905181Sgd78059 break;
25915181Sgd78059 }
25925181Sgd78059
25935181Sgd78059 dmfep = kmem_zalloc(sizeof (*dmfep), KM_SLEEP);
25945181Sgd78059 ddi_set_driver_private(devinfo, dmfep);
25955181Sgd78059 dmfep->devinfo = devinfo;
25965181Sgd78059 dmfep->dmfe_guard = DMFE_GUARD;
25975181Sgd78059
25985181Sgd78059 /*
25995181Sgd78059 * Initialize more fields in DMFE private data
26005181Sgd78059 * Determine the local MAC address
26015181Sgd78059 */
26025181Sgd78059 #if DMFEDEBUG
26035181Sgd78059 dmfep->debug = ddi_prop_get_int(DDI_DEV_T_ANY, devinfo, 0,
26045181Sgd78059 debug_propname, dmfe_debug);
26055181Sgd78059 #endif /* DMFEDEBUG */
26065181Sgd78059 dmfep->cycid = NULL;
26075181Sgd78059 (void) snprintf(dmfep->ifname, sizeof (dmfep->ifname), "dmfe%d",
26085181Sgd78059 instance);
26095181Sgd78059
26105181Sgd78059 /*
26115181Sgd78059 * Check for custom "opmode-reg-value" property;
26125181Sgd78059 * if none, use the defaults below for CSR6 ...
26135181Sgd78059 */
26145181Sgd78059 csr6 = TX_THRESHOLD_HI | STORE_AND_FORWARD | EXT_MII_IF | OPN_25_MB1;
26155181Sgd78059 dmfep->opmode = ddi_prop_get_int(DDI_DEV_T_ANY, devinfo,
26165181Sgd78059 DDI_PROP_DONTPASS, opmode_propname, csr6);
26175181Sgd78059
26185181Sgd78059 /*
26195181Sgd78059 * Read chip ID & set up config space command register(s)
26205181Sgd78059 */
26215181Sgd78059 if (dmfe_config_init(dmfep, &dmfep->chipid) != DDI_SUCCESS) {
26225181Sgd78059 dmfe_error(dmfep, "dmfe_config_init() failed");
26235181Sgd78059 goto attach_fail;
26245181Sgd78059 }
26255181Sgd78059
26265181Sgd78059 /*
26275181Sgd78059 * Map operating registers
26285181Sgd78059 */
26295181Sgd78059 err = ddi_regs_map_setup(devinfo, DMFE_PCI_RNUMBER,
26305181Sgd78059 &dmfep->io_reg, 0, 0, &dmfe_reg_accattr, &dmfep->io_handle);
26315181Sgd78059 if (err != DDI_SUCCESS) {
26325181Sgd78059 dmfe_error(dmfep, "ddi_regs_map_setup() failed");
26335181Sgd78059 goto attach_fail;
26345181Sgd78059 }
26355181Sgd78059
26365181Sgd78059 /*
26375181Sgd78059 * Get our MAC address.
26385181Sgd78059 */
26395181Sgd78059 dmfe_find_mac_address(dmfep);
26405181Sgd78059
26415181Sgd78059 /*
26425181Sgd78059 * Allocate the TX and RX descriptors/buffers.
26435181Sgd78059 */
26445181Sgd78059 dmfep->tx.n_desc = dmfe_tx_desc;
26455181Sgd78059 dmfep->rx.n_desc = dmfe_rx_desc;
26465181Sgd78059 err = dmfe_alloc_bufs(dmfep);
26475181Sgd78059 if (err != DDI_SUCCESS) {
26485181Sgd78059 goto attach_fail;
26495181Sgd78059 }
26505181Sgd78059
26515181Sgd78059 /*
26525181Sgd78059 * Add the softint handler
26535181Sgd78059 */
26545181Sgd78059 if (ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, &dmfep->factotum_id,
26555181Sgd78059 NULL, NULL, dmfe_factotum, (caddr_t)dmfep) != DDI_SUCCESS) {
26565181Sgd78059 dmfe_error(dmfep, "ddi_add_softintr() failed");
26575181Sgd78059 goto attach_fail;
26585181Sgd78059 }
26595181Sgd78059 dmfep->progress |= PROGRESS_SOFTINT;
26605181Sgd78059
26615181Sgd78059 /*
26625181Sgd78059 * Add the h/w interrupt handler & initialise mutexen
26635181Sgd78059 */
26649860Sgdamore@opensolaris.org if (ddi_get_iblock_cookie(devinfo, 0, &dmfep->iblk) != DDI_SUCCESS) {
26659860Sgdamore@opensolaris.org dmfe_error(dmfep, "ddi_get_iblock_cookie() failed");
26669860Sgdamore@opensolaris.org goto attach_fail;
26679860Sgdamore@opensolaris.org }
26689860Sgdamore@opensolaris.org
26699860Sgdamore@opensolaris.org mutex_init(dmfep->milock, NULL, MUTEX_DRIVER, NULL);
26709860Sgdamore@opensolaris.org mutex_init(dmfep->oplock, NULL, MUTEX_DRIVER, dmfep->iblk);
26719860Sgdamore@opensolaris.org mutex_init(dmfep->rxlock, NULL, MUTEX_DRIVER, dmfep->iblk);
26729860Sgdamore@opensolaris.org mutex_init(dmfep->txlock, NULL, MUTEX_DRIVER, dmfep->iblk);
26739860Sgdamore@opensolaris.org dmfep->progress |= PROGRESS_MUTEX;
26749860Sgdamore@opensolaris.org
26759860Sgdamore@opensolaris.org if (ddi_add_intr(devinfo, 0, NULL, NULL,
26765181Sgd78059 dmfe_interrupt, (caddr_t)dmfep) != DDI_SUCCESS) {
26775181Sgd78059 dmfe_error(dmfep, "ddi_add_intr() failed");
26785181Sgd78059 goto attach_fail;
26795181Sgd78059 }
26805181Sgd78059 dmfep->progress |= PROGRESS_HWINT;
26815181Sgd78059
26825181Sgd78059 /*
26835181Sgd78059 * Create & initialise named kstats
26845181Sgd78059 */
26855181Sgd78059 dmfe_init_kstats(dmfep, instance);
26865181Sgd78059
26875181Sgd78059 /*
26885181Sgd78059 * Reset & initialise the chip and the ring buffers
26895181Sgd78059 * Initialise the (internal) PHY
26905181Sgd78059 */
26915181Sgd78059 mutex_enter(dmfep->oplock);
26925181Sgd78059 mutex_enter(dmfep->rxlock);
26935181Sgd78059 mutex_enter(dmfep->txlock);
26945181Sgd78059
26955181Sgd78059 dmfe_reset(dmfep);
26965181Sgd78059
26975181Sgd78059 /*
26985181Sgd78059 * Prepare the setup packet
26995181Sgd78059 */
27005181Sgd78059 bzero(dmfep->tx_desc.setup_va, SETUPBUF_SIZE);
27015181Sgd78059 bzero(dmfep->mcast_refs, MCASTBUF_SIZE);
27025181Sgd78059 dmfep->addr_set = B_FALSE;
27035181Sgd78059 dmfep->opmode &= ~(PROMISC_MODE | PASS_MULTICAST);
27045181Sgd78059 dmfep->mac_state = DMFE_MAC_RESET;
27055181Sgd78059
27065181Sgd78059 mutex_exit(dmfep->txlock);
27075181Sgd78059 mutex_exit(dmfep->rxlock);
27085181Sgd78059 mutex_exit(dmfep->oplock);
27095181Sgd78059
27105181Sgd78059 if (dmfe_init_phy(dmfep) != B_TRUE)
27115181Sgd78059 goto attach_fail;
27125181Sgd78059
27135181Sgd78059 /*
27145181Sgd78059 * Send a reasonable setup frame. This configures our starting
27155181Sgd78059 * address and the broadcast address.
27165181Sgd78059 */
27175181Sgd78059 (void) dmfe_m_unicst(dmfep, dmfep->curr_addr);
27185181Sgd78059
27195181Sgd78059 /*
27205181Sgd78059 * Initialize pointers to device specific functions which
27215181Sgd78059 * will be used by the generic layer.
27225181Sgd78059 */
27235181Sgd78059 if ((macp = mac_alloc(MAC_VERSION)) == NULL)
27245181Sgd78059 goto attach_fail;
27255181Sgd78059 macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
27265181Sgd78059 macp->m_driver = dmfep;
27275181Sgd78059 macp->m_dip = devinfo;
27285181Sgd78059 macp->m_src_addr = dmfep->curr_addr;
27295181Sgd78059 macp->m_callbacks = &dmfe_m_callbacks;
27305181Sgd78059 macp->m_min_sdu = 0;
27315181Sgd78059 macp->m_max_sdu = ETHERMTU;
27325895Syz147064 macp->m_margin = VLAN_TAGSZ;
27335181Sgd78059
27345181Sgd78059 /*
27355181Sgd78059 * Finally, we're ready to register ourselves with the MAC layer
27365181Sgd78059 * interface; if this succeeds, we're all ready to start()
27375181Sgd78059 */
27385181Sgd78059 err = mac_register(macp, &dmfep->mh);
27395181Sgd78059 mac_free(macp);
27405181Sgd78059 if (err != 0)
27415181Sgd78059 goto attach_fail;
27425181Sgd78059 ASSERT(dmfep->dmfe_guard == DMFE_GUARD);
27435181Sgd78059
27445181Sgd78059 /*
27455181Sgd78059 * Install the cyclic callback that we use to check for link
27465181Sgd78059 * status, transmit stall, etc. The cyclic callback (dmfe_cyclic())
27475181Sgd78059 * is invoked in kernel context then.
27485181Sgd78059 */
27495181Sgd78059 ASSERT(dmfep->cycid == NULL);
27505181Sgd78059 dmfep->cycid = ddi_periodic_add(dmfe_cyclic, dmfep,
27515181Sgd78059 dmfe_tick_us * 1000, DDI_IPL_0);
27525181Sgd78059 return (DDI_SUCCESS);
27535181Sgd78059
27545181Sgd78059 attach_fail:
27555181Sgd78059 dmfe_unattach(dmfep);
27565181Sgd78059 return (DDI_FAILURE);
27575181Sgd78059 }
27585181Sgd78059
27595181Sgd78059 /*
27605181Sgd78059 * dmfe_suspend() -- suspend transmit/receive for powerdown
27615181Sgd78059 */
27625181Sgd78059 static int
dmfe_suspend(dmfe_t * dmfep)27635181Sgd78059 dmfe_suspend(dmfe_t *dmfep)
27645181Sgd78059 {
27655181Sgd78059 /*
27665181Sgd78059 * Just stop processing ...
27675181Sgd78059 */
27689860Sgdamore@opensolaris.org mii_suspend(dmfep->mii);
27695181Sgd78059 mutex_enter(dmfep->oplock);
27705181Sgd78059 dmfe_stop(dmfep);
27719860Sgdamore@opensolaris.org
27729860Sgdamore@opensolaris.org mutex_enter(dmfep->txlock);
27739860Sgdamore@opensolaris.org dmfep->suspended = B_TRUE;
27749860Sgdamore@opensolaris.org mutex_exit(dmfep->txlock);
27755181Sgd78059 mutex_exit(dmfep->oplock);
27765181Sgd78059
27775181Sgd78059 return (DDI_SUCCESS);
27785181Sgd78059 }
27795181Sgd78059
27805181Sgd78059 /*
27815181Sgd78059 * detach(9E) -- Detach a device from the system
27825181Sgd78059 */
27835181Sgd78059 static int
dmfe_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)27845181Sgd78059 dmfe_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
27855181Sgd78059 {
27865181Sgd78059 dmfe_t *dmfep;
27875181Sgd78059
27885181Sgd78059 dmfep = ddi_get_driver_private(devinfo);
27895181Sgd78059
27905181Sgd78059 switch (cmd) {
27915181Sgd78059 default:
27925181Sgd78059 return (DDI_FAILURE);
27935181Sgd78059
27945181Sgd78059 case DDI_SUSPEND:
27955181Sgd78059 return (dmfe_suspend(dmfep));
27965181Sgd78059
27975181Sgd78059 case DDI_DETACH:
27985181Sgd78059 break;
27995181Sgd78059 }
28005181Sgd78059
28015181Sgd78059 /*
28025181Sgd78059 * Unregister from the MAC subsystem. This can fail, in
28035181Sgd78059 * particular if there are DLPI style-2 streams still open -
28045181Sgd78059 * in which case we just return failure without shutting
28055181Sgd78059 * down chip operations.
28065181Sgd78059 */
28075181Sgd78059 if (mac_unregister(dmfep->mh) != DDI_SUCCESS)
28085181Sgd78059 return (DDI_FAILURE);
28095181Sgd78059
28105181Sgd78059 /*
28115181Sgd78059 * All activity stopped, so we can clean up & exit
28125181Sgd78059 */
28135181Sgd78059 dmfe_unattach(dmfep);
28145181Sgd78059 return (DDI_SUCCESS);
28155181Sgd78059 }
28165181Sgd78059
28175181Sgd78059
28185181Sgd78059 /*
28195181Sgd78059 * ========== Module Loading Data & Entry Points ==========
28205181Sgd78059 */
28215181Sgd78059
28225181Sgd78059 DDI_DEFINE_STREAM_OPS(dmfe_dev_ops, nulldev, nulldev, dmfe_attach, dmfe_detach,
28237656SSherry.Moore@Sun.COM nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
28245181Sgd78059
28255181Sgd78059 static struct modldrv dmfe_modldrv = {
28265181Sgd78059 &mod_driverops, /* Type of module. This one is a driver */
28275181Sgd78059 dmfe_ident, /* short description */
28285181Sgd78059 &dmfe_dev_ops /* driver specific ops */
28295181Sgd78059 };
28305181Sgd78059
28315181Sgd78059 static struct modlinkage modlinkage = {
28325181Sgd78059 MODREV_1, (void *)&dmfe_modldrv, NULL
28335181Sgd78059 };
28345181Sgd78059
28355181Sgd78059 int
_info(struct modinfo * modinfop)28365181Sgd78059 _info(struct modinfo *modinfop)
28375181Sgd78059 {
28385181Sgd78059 return (mod_info(&modlinkage, modinfop));
28395181Sgd78059 }
28405181Sgd78059
28415181Sgd78059 int
_init(void)28425181Sgd78059 _init(void)
28435181Sgd78059 {
28445181Sgd78059 uint32_t tmp100;
28455181Sgd78059 uint32_t tmp10;
28465181Sgd78059 int i;
28475181Sgd78059 int status;
28485181Sgd78059
28495181Sgd78059 /* Calculate global timing parameters */
28505181Sgd78059 tmp100 = (dmfe_tx100_stall_us+dmfe_tick_us-1)/dmfe_tick_us;
28515181Sgd78059 tmp10 = (dmfe_tx10_stall_us+dmfe_tick_us-1)/dmfe_tick_us;
28525181Sgd78059
28535181Sgd78059 for (i = 0; i <= TX_PROCESS_MAX_STATE; ++i) {
28545181Sgd78059 switch (i) {
28555181Sgd78059 case TX_PROCESS_STATE(TX_PROCESS_FETCH_DATA):
28565181Sgd78059 case TX_PROCESS_STATE(TX_PROCESS_WAIT_END):
28575181Sgd78059 /*
28585181Sgd78059 * The chip doesn't spontaneously recover from
28595181Sgd78059 * a stall in these states, so we reset early
28605181Sgd78059 */
28615181Sgd78059 stall_100_tix[i] = tmp100;
28625181Sgd78059 stall_10_tix[i] = tmp10;
28635181Sgd78059 break;
28645181Sgd78059
28655181Sgd78059 case TX_PROCESS_STATE(TX_PROCESS_SUSPEND):
28665181Sgd78059 default:
28675181Sgd78059 /*
28685181Sgd78059 * The chip has been seen to spontaneously recover
28695181Sgd78059 * after an apparent stall in the SUSPEND state,
28705181Sgd78059 * so we'll allow it rather longer to do so. As
28715181Sgd78059 * stalls in other states have not been observed,
28725181Sgd78059 * we'll use long timeouts for them too ...
28735181Sgd78059 */
28745181Sgd78059 stall_100_tix[i] = tmp100 * 20;
28755181Sgd78059 stall_10_tix[i] = tmp10 * 20;
28765181Sgd78059 break;
28775181Sgd78059 }
28785181Sgd78059 }
28795181Sgd78059
28805181Sgd78059 mac_init_ops(&dmfe_dev_ops, "dmfe");
28815181Sgd78059 status = mod_install(&modlinkage);
28825181Sgd78059 if (status == DDI_SUCCESS)
28835181Sgd78059 dmfe_log_init();
28845181Sgd78059
28855181Sgd78059 return (status);
28865181Sgd78059 }
28875181Sgd78059
28885181Sgd78059 int
_fini(void)28895181Sgd78059 _fini(void)
28905181Sgd78059 {
28915181Sgd78059 int status;
28925181Sgd78059
28935181Sgd78059 status = mod_remove(&modlinkage);
28945181Sgd78059 if (status == DDI_SUCCESS) {
28955181Sgd78059 mac_fini_ops(&dmfe_dev_ops);
28965181Sgd78059 dmfe_log_fini();
28975181Sgd78059 }
28985181Sgd78059
28995181Sgd78059 return (status);
29005181Sgd78059 }
2901