bge_main2.c revision dc3f9a75e59d7bf1277d035ab1a4875c720910f9
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "bge_impl.h"
#include <sys/mac_provider.h>
#include <sys/mac_flow.h>
/*
* This is the string displayed by modinfo, etc.
*/
static char bge_ident[] = "Broadcom Gb Ethernet";
/*
* Property names
*/
static char debug_propname[] = "bge-debug-flags";
static char clsize_propname[] = "cache-line-size";
static char latency_propname[] = "latency-timer";
static char localmac_boolname[] = "local-mac-address?";
static char localmac_propname[] = "local-mac-address";
static char macaddr_propname[] = "mac-address";
static char subdev_propname[] = "subsystem-id";
static char subven_propname[] = "subsystem-vendor-id";
static char rxrings_propname[] = "bge-rx-rings";
static char txrings_propname[] = "bge-tx-rings";
static char fm_cap[] = "fm-capable";
static char default_mtu[] = "default_mtu";
static int bge_add_intrs(bge_t *, int);
static void bge_rem_intrs(bge_t *);
static int bge_unicst_set(void *, const uint8_t *, int);
/*
* Describes the chip's DMA engine
*/
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_V0, /* dma_attr version */
0x0000000000000000ull, /* dma_attr_addr_lo */
0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */
0x00000000FFFFFFFFull, /* dma_attr_count_max */
0x0000000000000001ull, /* dma_attr_align */
0x00000FFF, /* dma_attr_burstsizes */
0x00000001, /* dma_attr_minxfer */
0x000000000000FFFFull, /* dma_attr_maxxfer */
0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x00000001, /* dma_attr_granular */
DDI_DMA_FLAGERR /* dma_attr_flags */
};
/*
* PIO access attributes for registers
*/
static ddi_device_acc_attr_t bge_reg_accattr = {
};
/*
* DMA access attributes for descriptors: NOT to be byte swapped.
*/
static ddi_device_acc_attr_t bge_desc_accattr = {
};
/*
* DMA access attributes for data: NOT to be byte swapped.
*/
static ddi_device_acc_attr_t bge_data_accattr = {
};
static int bge_m_start(void *);
static void bge_m_stop(void *);
static int bge_m_promisc(void *, boolean_t);
static int bge_unicst_set(void *, const uint8_t *,
int);
static int bge_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static int bge_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static void bge_m_propinfo(void *, const char *, mac_prop_id_t,
const void *);
void *);
static void bge_priv_propinfo(const char *,
static mac_callbacks_t bge_m_callbacks = {
NULL,
NULL,
NULL,
NULL,
};
char *bge_priv_prop[] = {
"_adv_asym_pause_cap",
"_adv_pause_cap",
"_drain_max",
"_msi_cnt",
"_rx_intr_coalesce_blank_time",
"_tx_intr_coalesce_blank_time",
"_rx_intr_coalesce_pkt_cnt",
"_tx_intr_coalesce_pkt_cnt",
};
/*
* ========== Transmit and receive ring reinitialisation ==========
*/
/*
* These <reinit> routines each reset the specified ring to an initial
* state, assuming that the corresponding <init> routine has already
* been called exactly once.
*/
static void
{
/*
* Reinitialise control variables ...
*/
srp->txfill_next = 0;
srp->txpkt_next = 0;
/*
* Initialize the tx buffer push queue
*/
txbuf_queue->count = 0;
/*
* Initialize the tx buffer pop queue
*/
txbuf_queue->count = 0;
txbuf_queue->count++;
txbuf++;
txbuf_head++;
}
/*
* Zero and sync all the h/w Send Buffer Descriptors
*/
}
static void
{
/*
* Reinitialise control variables ...
*/
}
static void
{
};
/*
* Zero, initialise and sync all the h/w Receive Buffer Descriptors
* Note: all the remaining fields (<type>, <flags>, <ip_cksum>,
* <tcp_udp_cksum>, <error_flag>, <vlan_tag>, and <reserved>)
* should be zeroed, and so don't need to be set up specifically
* once the whole area has been cleared.
*/
}
/*
* Finally, reinitialise the ring control variables ...
*/
}
/*
* Reinitialize all rings
*/
static void
{
/*
* Send Rings ...
*/
/*
* Receive Return Rings ...
*/
/*
* Receive Producer Rings ...
*/
}
/*
* ========== Internal state management entry points ==========
*/
/*
* These routines provide all the functionality required by the
* corresponding GLD entry points, but don't update the GLD state
* so they can be called internally without disturbing our record
* of what GLD thinks we should be doing ...
*/
/*
* bge_reset() -- reset h/w & rings to initial state
*/
static int
#ifdef BGE_IPMI_ASF
#else
#endif
{
int retval;
/*
* Grab all the other mutexes in the world (this should
* ensure no other threads are manipulating driver state)
*/
#ifdef BGE_IPMI_ASF
#else
#endif
/*
* Free the world ...
*/
return (retval);
}
/*
* bge_stop() -- stop processing, don't reset h/w or rings
*/
static void
{
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
} else {
#endif
#ifdef BGE_IPMI_ASF
}
#endif
}
/*
* bge_start() -- start transmitting/receiving
*/
static int
{
int retval;
/*
* Start chip processing, including enabling interrupts
*/
return (retval);
}
/*
* bge_restart - restart transmitting/receiving after error or suspend
*/
int
{
int retval = DDI_SUCCESS;
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
} else
#else
#endif
}
return (retval);
}
/*
* ========== Nemo-required management entry points ==========
*/
/*
* bge_m_stop() -- stop transmitting/receiving
*/
static void
bge_m_stop(void *arg)
{
/*
* Just stop processing, then record new GLD state
*/
/* can happen during autorecovery */
} else
bgep->link_update_timer = 0;
/*
* Free the possible tx buffers allocated in tx process.
*/
#ifdef BGE_IPMI_ASF
if (!bgep->asf_pseudostop)
#endif
{
}
}
}
/*
* bge_m_start() -- start transmitting/receiving
*/
static int
bge_m_start(void *arg)
{
/*
* Start processing and record new GLD state
*/
/* can happen during autorecovery */
return (EIO);
}
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
(bgep->asf_pseudostop)) {
return (0);
}
}
#else
#endif
return (EIO);
}
return (EIO);
}
return (EIO);
}
return (EIO);
}
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
/* start ASF heart beat */
(void *)bgep,
}
}
#endif
return (0);
}
/*
* bge_unicst_set() -- set the physical network address
*/
static int
{
ether_sprintf((void *)macaddr)));
/*
* Remember the new current address in the driver state
* Sync the chip's idea of the address too ...
*/
/* can happen during autorecovery */
return (EIO);
}
#ifdef BGE_IPMI_ASF
#else
#endif
return (EIO);
}
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
/*
* The above bge_chip_sync() function wrote the ethernet MAC
*/
/*
* We must stop ASF heart beat before bge_chip_stop(),
* otherwise some computers (ex. IBM HS20 blade server)
* may crash.
*/
}
return (EIO);
}
/*
* Start our ASF heartbeat counter as soon as possible.
*/
/* start ASF heart beat */
(void *)bgep,
}
}
#endif
return (EIO);
}
return (EIO);
}
return (0);
}
extern void bge_wake_factotum(bge_t *);
static boolean_t
{
/*
* All adv_* parameters are locked (read-only) while
* the device is in any sort of loopback mode ...
*/
switch (pr_num) {
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_EN_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
case MAC_PROP_EN_1000HDX_CAP:
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_EN_100FDX_CAP:
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_EN_100HDX_CAP:
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_EN_10FDX_CAP:
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_EN_10HDX_CAP:
case MAC_PROP_AUTONEG:
case MAC_PROP_FLOWCTRL:
return (B_TRUE);
}
return (B_FALSE);
}
/*
*/
static int
{
int err = 0;
/*
* All adv_* parameters are locked (read-only)
* while the device is in any sort of loopback mode.
*/
return (EBUSY);
}
((pr_num == MAC_PROP_EN_100FDX_CAP) ||
(pr_num == MAC_PROP_EN_100HDX_CAP) ||
(pr_num == MAC_PROP_EN_10FDX_CAP) ||
(pr_num == MAC_PROP_EN_10HDX_CAP))) {
/*
* read-only and 0 on serdes
*/
return (ENOTSUP);
}
if (DEVICE_5906_SERIES_CHIPSETS(bgep) &&
((pr_num == MAC_PROP_EN_1000FDX_CAP) ||
(pr_num == MAC_PROP_EN_1000HDX_CAP))) {
return (ENOTSUP);
}
switch (pr_num) {
case MAC_PROP_EN_1000FDX_CAP:
goto reprogram;
case MAC_PROP_EN_1000HDX_CAP:
goto reprogram;
case MAC_PROP_EN_100FDX_CAP:
goto reprogram;
case MAC_PROP_EN_100HDX_CAP:
goto reprogram;
case MAC_PROP_EN_10FDX_CAP:
goto reprogram;
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_STATUS:
case MAC_PROP_SPEED:
case MAC_PROP_DUPLEX:
break;
case MAC_PROP_AUTONEG:
break;
case MAC_PROP_MTU:
err = 0;
break;
}
if (new_mtu < BGE_DEFAULT_MTU ||
new_mtu > BGE_MAXIMUM_MTU) {
break;
}
if ((new_mtu > BGE_DEFAULT_MTU) &&
break;
}
break;
}
if (bge_chip_id_init(bgep)) {
break;
}
err = 0;
break;
case MAC_PROP_FLOWCTRL:
switch (fl) {
default:
break;
case LINK_FLOWCTRL_NONE:
bgep->param_adv_pause = 0;
bgep->param_adv_asym_pause = 0;
break;
case LINK_FLOWCTRL_RX:
break;
case LINK_FLOWCTRL_TX:
bgep->param_adv_pause = 0;
break;
case LINK_FLOWCTRL_BI:
bgep->param_adv_asym_pause = 0;
break;
}
if (err == 0) {
}
break;
case MAC_PROP_PRIVATE:
pr_val);
break;
default:
break;
}
return (err);
}
/* ARGSUSED */
static int
{
int err = 0;
switch (pr_num) {
case MAC_PROP_DUPLEX:
sizeof (link_duplex_t));
break;
case MAC_PROP_SPEED: {
break;
}
case MAC_PROP_STATUS:
sizeof (link_state_t));
break;
case MAC_PROP_AUTONEG:
break;
case MAC_PROP_FLOWCTRL: {
if (bgep->param_link_rx_pause &&
if (!bgep->param_link_rx_pause &&
if (!bgep->param_link_rx_pause &&
if (bgep->param_link_rx_pause &&
break;
}
case MAC_PROP_ADV_1000FDX_CAP:
break;
case MAC_PROP_EN_1000FDX_CAP:
break;
case MAC_PROP_ADV_1000HDX_CAP:
break;
case MAC_PROP_EN_1000HDX_CAP:
break;
case MAC_PROP_ADV_100FDX_CAP:
break;
case MAC_PROP_EN_100FDX_CAP:
break;
case MAC_PROP_ADV_100HDX_CAP:
break;
case MAC_PROP_EN_100HDX_CAP:
break;
case MAC_PROP_ADV_10FDX_CAP:
break;
case MAC_PROP_EN_10FDX_CAP:
break;
case MAC_PROP_ADV_10HDX_CAP:
break;
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_ADV_100T4_CAP:
case MAC_PROP_EN_100T4_CAP:
break;
case MAC_PROP_PRIVATE:
pr_valsize, pr_val);
return (err);
default:
return (ENOTSUP);
}
return (0);
}
static void
{
/*
* otherwise by the driver.
*/
switch (pr_num) {
case MAC_PROP_DUPLEX:
case MAC_PROP_SPEED:
case MAC_PROP_STATUS:
case MAC_PROP_ADV_1000FDX_CAP:
case MAC_PROP_ADV_1000HDX_CAP:
case MAC_PROP_ADV_100FDX_CAP:
case MAC_PROP_ADV_100HDX_CAP:
case MAC_PROP_ADV_10FDX_CAP:
case MAC_PROP_ADV_10HDX_CAP:
case MAC_PROP_ADV_100T4_CAP:
case MAC_PROP_EN_100T4_CAP:
break;
case MAC_PROP_EN_1000FDX_CAP:
case MAC_PROP_EN_1000HDX_CAP:
else
break;
case MAC_PROP_EN_100FDX_CAP:
case MAC_PROP_EN_100HDX_CAP:
case MAC_PROP_EN_10FDX_CAP:
case MAC_PROP_EN_10HDX_CAP:
break;
case MAC_PROP_AUTONEG:
break;
case MAC_PROP_FLOWCTRL:
break;
case MAC_PROP_MTU:
(flags & CHIP_FLAG_NO_JUMBO) ?
break;
case MAC_PROP_PRIVATE:
break;
}
bge_param_locked(pr_num)) ||
((pr_num == MAC_PROP_EN_100FDX_CAP) ||
(pr_num == MAC_PROP_EN_100HDX_CAP) ||
(pr_num == MAC_PROP_EN_10FDX_CAP) ||
(pr_num == MAC_PROP_EN_10HDX_CAP))) ||
((pr_num == MAC_PROP_EN_1000FDX_CAP) ||
(pr_num == MAC_PROP_EN_1000HDX_CAP))))
}
/* ARGSUSED */
static int
const void *pr_val)
{
int err = 0;
long result;
} else {
}
return (err);
}
} else {
}
return (err);
}
/*
* on the Tx side, we need to update the h/w register for
* real packet transmission per packet. The drain_max parameter
* is used to reduce the register access. This parameter
* controls the max number of packets that we will hold before
* updating the bge h/w to trigger h/w transmit. The bge
* chipset usually has a max of 512 Tx descriptors, thus
* the upper bound on drain_max is 512.
*/
return (err);
}
else {
}
return (err);
}
return (err);
}
else {
}
return (err);
}
return (EINVAL);
if (result < 0)
else {
}
return (err);
}
return (EINVAL);
if (result < 0)
else {
}
return (err);
}
return (EINVAL);
if (result < 0)
else {
}
return (err);
}
return (EINVAL);
if (result < 0)
else {
}
return (err);
}
return (ENOTSUP);
}
static int
void *pr_val)
{
int value;
else
return (ENOTSUP);
return (0);
}
static void
{
char valstr[64];
int value;
value = 1;
value = 1;
value = 64;
value = 0;
else
return;
}
/*
* Compute the index of the required bit in the multicast hash map.
* This must mirror the way the hardware actually does it!
* See Broadcom document 570X-PG102-R page 125.
*/
static uint32_t
{
return (hash);
}
/*
*/
static int
{
/*
* Precalculate all required masks, pointers etc ...
*/
BGE_DEBUG(("bge_m_multicst: hash 0x%x index %d (%d:0x%x) = %d",
/*
* We must set the appropriate bit in the hash map (and the
* corresponding h/w register) when the refcount goes from 0
* to >0, and clear it when the last ref goes away (refcount
* goes from >0 back to 0). If we change the hash map, we
* must also update the chip's hardware map registers.
*/
/* can happen during autorecovery */
return (EIO);
}
if (add) {
if ((*refp)++ == 0) {
#ifdef BGE_IPMI_ASF
#else
#endif
(void) bge_check_acc_handle(bgep,
bgep->cfg_handle);
(void) bge_check_acc_handle(bgep,
return (EIO);
}
}
} else {
if (--(*refp) == 0) {
#ifdef BGE_IPMI_ASF
#else
#endif
(void) bge_check_acc_handle(bgep,
bgep->cfg_handle);
(void) bge_check_acc_handle(bgep,
return (EIO);
}
}
}
return (EIO);
}
return (EIO);
}
return (0);
}
/*
* bge_m_promisc() -- set or reset promiscuous mode on the board
*
* receive-all-multicast modes.
*/
static int
{
/*
* Store MAC layer specified mode and pass to chip layer to update h/w
*/
/* can happen during autorecovery */
return (EIO);
}
#ifdef BGE_IPMI_ASF
#else
#endif
return (EIO);
}
return (EIO);
}
return (EIO);
}
return (0);
}
/*
* Find the slot for the specified unicast address
*/
int
{
int slot;
return (slot);
}
return (-1);
}
/*
* Programs the classifier to start steering packets matching 'mac_addr' to the
* specified ring 'arg'.
*/
static int
{
int i;
int slot;
int err;
if (bgep->unicst_addr_avail == 0) {
return (ENOSPC);
}
/*
* First add the unicast address to a available slot.
*/
break;
}
}
goto fail;
/* A rule is already here. Deny this. */
goto fail;
}
/*
* Allocate a bge_rule_info_t to keep track of which rule slots
* are being used.
*/
goto fail;
}
/*
* Look for the starting slot to place the rules.
* The two slots we reserve must be contiguous.
*/
for (i = 0; i + 1 < RECV_RULES_NUM_MAX; i++)
break;
return (0);
fail:
/* Clear the address just set */
return (err);
}
/*
* Stop classifying packets matching the MAC address to the specified ring.
*/
static int
{
int start;
int slot;
int err;
/*
* Remove the MAC address from its slot.
*/
if (slot == -1) {
return (EINVAL);
}
return (err);
return (EINVAL);
start++;
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
/*
* Callback funtion for MAC layer to register all rings
* for given ring_group, noted by rg_index.
*/
void
{
switch (rtype) {
case MAC_RING_TYPE_RX: {
MAC_ADDRESS_REGS_MAX) && index == 0);
break;
}
case MAC_RING_TYPE_TX:
default:
ASSERT(0);
break;
}
}
/*
* Fill infop passed as argument
* fill in respective ring_group info
* Each group has a single ring in it. We keep it simple
* and use the same internal handle for rings and groups.
*/
void
{
switch (rtype) {
case MAC_RING_TYPE_RX: {
break;
}
case MAC_RING_TYPE_TX:
default:
ASSERT(0);
break;
}
}
/*ARGSUSED*/
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
break;
}
case MAC_CAPAB_RINGS: {
/* Temporarily disable multiple tx rings. */
return (B_FALSE);
break;
}
default:
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Loopback ioctl code
*/
static lb_property_t loopmodes[] = {
};
static enum ioc_reply
{
/*
* If the mode isn't being changed, there's nothing to do ...
*/
return (IOC_ACK);
/*
* Validate the requested mode and prepare a suitable message
* probably induce ...
*/
switch (mode) {
default:
return (IOC_INVAL);
case BGE_LOOP_NONE:
case BGE_LOOP_EXTERNAL_1000:
case BGE_LOOP_EXTERNAL_100:
case BGE_LOOP_EXTERNAL_10:
case BGE_LOOP_INTERNAL_PHY:
case BGE_LOOP_INTERNAL_MAC:
break;
}
/*
* All OK; tell the caller to reprogram
*/
return (IOC_RESTART_ACK);
}
static enum ioc_reply
{
int cmd;
/*
* Validate format of ioctl
*/
return (IOC_INVAL);
switch (cmd) {
default:
/* NOTREACHED */
return (IOC_INVAL);
case LB_GET_INFO_SIZE:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_GET_INFO:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_GET_MODE:
return (IOC_INVAL);
return (IOC_REPLY);
case LB_SET_MODE:
return (IOC_INVAL);
}
}
/*
* Specific bge IOCTLs, the gld module handles the generic ones.
*/
static void
{
int err;
int cmd;
/*
* Validate the command before bothering with the mutex ...
*/
switch (cmd) {
default:
return;
case BGE_MII_READ:
case BGE_MII_WRITE:
case BGE_SEE_READ:
case BGE_SEE_WRITE:
case BGE_FLASH_READ:
case BGE_FLASH_WRITE:
case BGE_DIAG:
case BGE_PEEK:
case BGE_POKE:
case BGE_PHY_RESET:
case BGE_SOFT_RESET:
case BGE_HARD_RESET:
break;
case LB_GET_INFO_SIZE:
case LB_GET_INFO:
case LB_GET_MODE:
/* FALLTHRU */
case LB_SET_MODE:
break;
}
if (need_privilege) {
/*
* Check for specific net_config privilege on Solaris 10+.
*/
if (err != 0) {
return;
}
}
/* can happen during autorecovery */
return;
}
switch (cmd) {
default:
break;
case BGE_MII_READ:
case BGE_MII_WRITE:
case BGE_SEE_READ:
case BGE_SEE_WRITE:
case BGE_FLASH_READ:
case BGE_FLASH_WRITE:
case BGE_DIAG:
case BGE_PEEK:
case BGE_POKE:
case BGE_PHY_RESET:
case BGE_SOFT_RESET:
case BGE_HARD_RESET:
break;
case LB_GET_INFO_SIZE:
case LB_GET_INFO:
case LB_GET_MODE:
case LB_SET_MODE:
break;
}
/*
* Do it now, while we still have the mutex.
*
* Note: update the PHY first, 'cos it controls the
*/
switch (status) {
case IOC_RESTART_REPLY:
case IOC_RESTART_ACK:
break;
}
}
}
/*
* Finally, decide how to reply
*/
switch (status) {
default:
case IOC_INVAL:
/*
* Error, reply with a NAK and EINVAL or the specified error
*/
break;
case IOC_DONE:
/*
* OK, reply already sent
*/
break;
case IOC_RESTART_ACK:
case IOC_ACK:
/*
* OK, reply with an ACK
*/
break;
case IOC_RESTART_REPLY:
case IOC_REPLY:
/*
* OK, send prepared reply as ACK or NAK
*/
break;
}
}
/*
*/
/*
* Allocate an area of memory and a DMA handle for accessing it
*/
static int
{
int err;
BGE_TRACE(("bge_alloc_dma_mem($%p, %ld, $%p, 0x%x, $%p)",
/*
* Allocate handle
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Allocate memory
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Bind the two together
*/
BGE_DEBUG(("bge_alloc_dma_mem(): bind %d bytes; err %d, %d cookies",
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Free one allocated area of DMAable memory
*/
static void
{
}
}
}
}
/*
* Utility routine to carve a slice off a chunk of allocated memory,
* updating the chunk descriptor accordingly. The size of the slice
* is given by the product of the <qty> and <size> parameters.
*/
static void
{
}
/*
* Initialise the specified Receive Producer (Buffer) Ring, using
* the information in the <dma_area> descriptors that it contains
* to set up all the other fields. This routine should be called
* only once for each ring.
*/
static void
{
};
};
};
BGE_TRACE(("bge_init_buff_ring($%p, %d)",
/*
* Set up the copy of the h/w RCB
*
* Note: unlike Send & Receive Return Rings, (where the max_len
* field holds the number of slots), in a Receive Buffer Ring
* this field indicates the size of each buffer in the ring.
*/
/*
* Other one-off initialisation of per-ring data
*/
/*
* Allocate the array of s/w Receive Buffer Descriptors
*/
/*
* Now initialise each array element once and for all
*/
}
}
/*
* Clean up initialisation done above before the memory is freed
*/
static void
{
BGE_TRACE(("bge_fini_buff_ring($%p, %d)",
}
/*
* Initialise the specified Receive (Return) Ring, using the
* information in the <dma_area> descriptors that it contains
* to set up all the other fields. This routine should be called
* only once for each ring.
*/
static void
{
BGE_TRACE(("bge_init_recv_ring($%p, %d)",
/*
* The chip architecture requires that receive return rings have
* 512 or 1024 or 2048 elements per ring. See 570X-PG108-R page 103.
*/
/*
* Set up the copy of the h/w RCB
*/
/*
* Other one-off initialisation of per-ring data
*/
}
/*
* Clean up initialisation done above before the memory is freed
*/
static void
{
BGE_TRACE(("bge_fini_recv_ring($%p, %d)",
if (rrp->rx_softint)
}
/*
* Initialise the specified Send Ring, using the information in the
* <dma_area> descriptors that it contains to set up all the other
* fields. This routine should be called only once for each ring.
*/
static void
{
BGE_TRACE(("bge_init_send_ring($%p, %d)",
/*
* The chip architecture requires that host-based send rings
* have 512 elements per ring. See 570X-PG102-R page 56.
*/
/*
* Set up the copy of the h/w RCB
*/
/*
* Other one-off initialisation of per-ring data
*/
if (nslots == 0)
return;
/*
* Allocate the array of s/w Send Buffer Descriptors
*/
srp->txbuf_head =
else
/*
* Chunk tx desc area
*/
sizeof (bge_sbd_t));
}
/*
* Chunk tx buffer area
*/
txbuf++;
}
}
}
/*
* Clean up initialisation done above before the memory is freed
*/
static void
{
BGE_TRACE(("bge_fini_send_ring($%p, %d)",
if (nslots == 0)
return;
}
/*
* Initialise all transmit, receive, and buffer rings.
*/
void
{
/*
* Perform one-off initialisation of each ring ...
*/
}
/*
* Undo the work of bge_init_rings() above before the memory is freed
*/
void
{
}
/*
* Called from the bge_m_stop() to free the tx buffers which are
* allocated from the tx process.
*/
void
{
/*
* Free the extra tx buffer DMA area
*/
/*
* Restore initial tx buffer numbers
*/
}
/*
* Called from tx process to allocate more tx buffers
*/
{
return (NULL);
/*
* Allocate memory & handles for TX buffers
*/
if (err != DDI_SUCCESS) {
/* Free the last already allocated OK chunks */
srp->tx_alloc_fail++;
return (NULL);
}
}
/*
* Chunk tx buffer area
*/
txbuf++;
}
}
/*
* Add above buffers to the tx buffer pop queue
*/
txbuf++;
txbuf_item++;
}
txbuf_item++;
return (txbuf_item_rtn);
}
/*
* This function allocates all the transmit and receive buffers
* and descriptors, in four chunks.
*/
int
{
int split;
int err;
BGE_TRACE(("bge_alloc_bufs($%p)",
(void *)bgep));
txbuffsize *= tx_rings;
rxdescsize *= sizeof (bge_rbd_t);
rxbuffdescsize *= sizeof (bge_rbd_t);
txdescsize *= sizeof (bge_sbd_t);
txdescsize += sizeof (bge_statistics_t);
txdescsize += sizeof (bge_status_t);
/*
*/
if (bge_relaxed_ordering)
/*
* Allocate memory & handles for RX buffers
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
}
/*
* Allocate memory & handles for TX buffers
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
}
/*
* Allocate memory & handles for receive return rings
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
}
/*
* Allocate memory & handles for buffer (producer) descriptor rings
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Allocate memory & handles for TX descriptor rings,
* status block, and statistics area
*/
if (err != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Now carve up each of the allocated areas ...
*/
}
}
0, sizeof (bge_rbd_t));
BGE_STD_SLOTS_USED, sizeof (bge_rbd_t));
BGE_MINI_SLOTS_USED, sizeof (bge_rbd_t));
BGE_SEND_SLOTS_USED, sizeof (bge_sbd_t));
0, sizeof (bge_sbd_t));
return (DDI_SUCCESS);
}
/*
* This routine frees the transmit and receive buffers and descriptors.
* Make sure the chip is stopped before calling it!
*/
void
{
int split;
BGE_TRACE(("bge_free_bufs($%p)",
(void *)bgep));
}
/*
* Determine (initial) MAC address ("BIA") to use for this interface
*/
static void
{
struct ether_addr sysaddr;
int *ints;
int err;
BGE_TRACE(("bge_find_mac_address($%p)",
(void *)bgep));
BGE_DEBUG(("bge_find_mac_address: hw_mac_addr %012llx, => %s (%sset)",
/*
* The "vendor's factory-set address" may already have
* been extracted from the chip, but if the property
* "local-mac-address" is set we use that instead. It
* will normally be set by OBP, but it could also be
* specified in a .conf file(!)
*
* There doesn't seem to be a way to define byte-array
* properties in a .conf, so we check whether it looks
* like an array of 6 ints instead.
*
* Then, we check whether it looks like an array of 6
* bytes (which it should, if OBP set it). If we can't
* make sense of it either way, we'll ignore it.
*/
if (err == DDI_PROP_SUCCESS) {
if (nelts == ETHERADDRL) {
while (nelts--)
}
}
if (err == DDI_PROP_SUCCESS) {
if (nelts == ETHERADDRL) {
while (nelts--)
}
}
BGE_DEBUG(("bge_find_mac_address: +local %s (%sset)",
/*
* Look up the OBP property "local-mac-address?". Note that even
* though its value is a string (which should be "true" or "false"),
* it can't be decoded by ddi_prop_lookup_string(9F). So, we zero
* the buffer first and then fetch the property as an untyped array;
* this may or may not include a final NUL, but since there will
* always be one left at the end of the buffer we can now treat it
* as a string anyway.
*/
/*
* Now, if the address still isn't set from the hardware (SEEPROM)
* or the OBP or .conf property, OR if the user has foolishly set
* 'local-mac-address? = false', use "the system address" instead
* (but only if it's non-null i.e. has been set from the IDPROM).
*/
}
BGE_DEBUG(("bge_find_mac_address: +system %s (%sset)",
/*
* Finally(!), if there's a valid "mac-address" property (created
* if we netbooted from this interface), we must use this instead
* get confused by the address changing as Solaris takes over!
*/
if (err == DDI_PROP_SUCCESS) {
if (nelts == ETHERADDRL) {
while (nelts--)
}
}
BGE_DEBUG(("bge_find_mac_address: =final %s (%sset)",
}
/*ARGSUSED*/
int
{
return (de.fme_status);
}
/*ARGSUSED*/
int
{
return (de.fme_status);
}
/*
* The IO fault service error handling callback function
*/
/*ARGSUSED*/
static int
{
/*
* as the driver can always deal with an error in any dma or
* access handle, we can just return the fme_status value.
*/
return (err->fme_status);
}
static void
{
/* Only register with IO Fault Services if we have some capability */
if (bgep->fm_capabilities) {
/* Register capabilities with IO Fault Services */
/*
* Initialize pci ereport capabilities if ereport capable
*/
/*
* Register error callback if error callback capable
*/
bge_fm_error_cb, (void*) bgep);
} else {
/*
* These fields have to be cleared of FMA if there are no
* FMA capabilities at runtime.
*/
dma_attr.dma_attr_flags = 0;
}
}
static void
{
/* Only unregister FMA capabilities if we registered some */
if (bgep->fm_capabilities) {
/*
* Release any resources allocated by pci_ereport_setup()
*/
/*
* Un-register error callback if error callback capable
*/
/* Unregister from IO Fault Services */
}
}
static void
#ifdef BGE_IPMI_ASF
#else
#endif
{
BGE_TRACE(("bge_unattach($%p)",
(void *)bgep));
/*
* Flag that no more activity may be initiated
*/
/*
* Quiesce the PHY and MAC (leave it reset but still powered).
* Clean up and free all BGE data structures
*/
}
#ifdef BGE_IPMI_ASF
#else
#endif
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
/*
* This register has been overlaid. We restore its
* initial value here.
*/
}
#endif
}
}
}
}
static int
{
return (DDI_FAILURE);
/*
* Refuse to resume if the data structures aren't consistent
*/
return (DDI_FAILURE);
#ifdef BGE_IPMI_ASF
/*
* Power management hasn't been supported in BGE now. If you
* code here.
*/
#endif
/*
* Read chip ID & set up config space command register(s)
* Refuse to resume if the chip has changed its identity!
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* All OK, reinitialise h/w & kick off GLD scheduling
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* attach(9E) -- Attach a device to the system
*
* Called once for each board successfully probed.
*/
static int
{
int instance;
int err;
int intr_types;
#ifdef BGE_IPMI_ASF
#ifdef __sparc
#endif
#ifdef BGE_NETCONSOLE
int retval;
#endif
#endif
BGE_GTRACE(("bge_attach($%p, %d) instance %d",
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_RESUME:
return (bge_resume(devinfo));
case DDI_ATTACH:
break;
}
bgep->param_msi_cnt = 0;
bgep->param_loop_mode = 0;
/*
* Initialize more fields in BGE private data
*/
/*
* Initialize for fma support
*/
/*
* Look up the IOMMU's page size for DVMA mappings (must be
* a power of 2) and convert to a mask. This can be used to
* determine whether a message buffer crosses a page boundary.
* Note: in 2s complement binary notation, if X is a power of
* 2, then -X has the representation "11...1100...00".
*/
/*
* Map config space registers
* Read chip ID & set up config space command register(s)
*
* Note: this leaves the chip accessible by Memory Space
* accesses, but with interrupts and Bus Mastering off.
* This should ensure that nothing untoward will happen
* if it has been left active by the (net-)bootloader.
* We'll re-enable Bus Mastering once we've reset the chip,
* and allow interrupts only when everything else is set up.
*/
#ifdef BGE_IPMI_ASF
#ifdef __sparc
/*
* We need to determine the type of chipset for accessing some configure
* registers. (This information will be used by bge_ind_put32,
* bge_ind_get32 and bge_nic_read32)
*/
/*
* For some chipsets (e.g., BCM5718), if MHCR_ENABLE_ENDIAN_BYTE_SWAP
* has been set in PCI_CONF_COMM already, we need to write the
* byte-swapped value to it. So we just write zero first for simplicity.
*/
#else
#endif
if (mhcrValue & MHCR_ENABLE_ENDIAN_WORD_SWAP) {
} else {
}
#endif
if (err != DDI_SUCCESS) {
goto attach_fail;
}
goto attach_fail;
}
#ifdef BGE_IPMI_ASF
if (DEVICE_5721_SERIES_CHIPSETS(bgep) ||
} else {
}
#endif
/*
* Update those parts of the chip ID derived from volatile
* registers with the values seen by OBP (in case the chip
* has been reset externally and therefore lost them).
*/
}
/*
* Map operating registers
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Characterise the device, so we know its requirements.
* Then allocate the appropriate TX and RX descriptors & buffers.
*/
goto attach_fail;
}
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/*
* Add the softint handlers:
*
* Both of these handlers are used to avoid restrictions on the
* particular, the hardware interrupt handler and its subfunctions
* can detect a number of conditions that we don't want to handle
* in that context or with that set of mutexes held. So, these
* softints are triggered instead:
*
* the <resched> softint is triggered if we have previously
* had to refuse to send a packet because of resource shortage
* (we've run out of transmit buffers), but the send completion
* interrupt handler has now detected that more buffers have
* become available.
*
* the <factotum> is triggered if the h/w interrupt handler
* sees the <link state changed> or <error> bits in the status
* block. It's also triggered periodically to poll the link
* state, just in case we aren't getting link status change
* interrupts ...
*/
if (err != DDI_SUCCESS) {
goto attach_fail;
}
if (err != DDI_SUCCESS) {
goto attach_fail;
}
/* Get supported interrupt types */
goto attach_fail;
}
BGE_DEBUG(("%s: ddi_intr_get_supported_types() returned: %x",
"trying FIXED interrupt type\n");
} else {
BGE_DEBUG(("%s: Using MSI interrupt type",
}
}
(intr_types & DDI_INTR_TYPE_FIXED)) {
"registration failed\n");
goto attach_fail;
}
}
goto attach_fail;
}
/*
* Note that interrupts are not enabled yet as
* mutex locks are not initialized. Initialize mutex locks.
*/
/*
* Initialize rings.
*/
/*
* Now that mutex locks are initialized, enable interrupts.
*/
/*
* Initialise link state variables
* Stop, reset & reinitialise the chip.
* Initialise the (internal) PHY.
*/
/*
* Reset chip & rings to initial state; also reset address
* filtering, promiscuity, loopback mode.
*/
#ifdef BGE_IPMI_ASF
#ifdef BGE_NETCONSOLE
#else
#endif
#else
#endif
goto attach_fail;
}
#ifdef BGE_IPMI_ASF
if (bgep->asf_enabled) {
}
#endif
goto attach_fail;
}
goto attach_fail;
}
goto attach_fail;
}
/*
* initialize NDD-tweakable parameters
*/
if (bge_nd_init(bgep)) {
goto attach_fail;
}
/*
* Create & initialise named kstats
*/
/*
* Determine whether to override the chip's own MAC address
*/
goto attach_fail;
/*
* Finally, we're ready to register ourselves with the MAC layer
* interface; if this succeeds, we're all ready to start()
*/
if (err != 0)
goto attach_fail;
/*
* Register a periodical handler.
* bge_chip_cyclic() is invoked in kernel context.
*/
#ifdef BGE_IPMI_ASF
#ifdef BGE_NETCONSOLE
if (bgep->asf_enabled) {
if (retval != DDI_SUCCESS)
goto attach_fail;
}
#endif
#endif
return (DDI_SUCCESS);
#ifdef BGE_IPMI_ASF
#else
#endif
return (DDI_FAILURE);
}
/*
*/
static int
{
/*
* Stop processing and idle (powerdown) the PHY ...
*/
#ifdef BGE_IPMI_ASF
/*
* Power management hasn't been supported in BGE now. If you
* code here.
*/
#endif
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* quiesce(9E) entry point.
*
* This function is called when the system is single-threaded at high
* PIL with preemption disabled. Therefore, this function must not be
* blocked.
*
* This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
* DDI_FAILURE indicates an error condition and should almost never happen.
*/
#ifdef __sparc
#define bge_quiesce ddi_quiesce_not_supported
#else
static int
{
return (DDI_FAILURE);
} else {
}
/* Stop the chip */
return (DDI_SUCCESS);
}
#endif
/*
* detach(9E) -- Detach a device from the system
*/
static int
{
#ifdef BGE_IPMI_ASF
#endif
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_SUSPEND:
return (bge_suspend(bgep));
case DDI_DETACH:
break;
}
#ifdef BGE_IPMI_ASF
}
if (bgep->asf_pseudostop) {
}
}
#endif
/*
* Unregister from the GLD subsystem. This can fail, in
* particular if there are DLPI style-2 streams still open -
* in which case we just return failure without shutting
* down chip operations.
*/
return (DDI_FAILURE);
/*
* All activity stopped, so we can clean up & exit
*/
#ifdef BGE_IPMI_ASF
#else
#endif
return (DDI_SUCCESS);
}
/*
* ========== Module Loading Data & Entry Points ==========
*/
nulldev, /* identify */
nulldev, /* probe */
bge_attach, /* attach */
bge_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
D_MP, /* bus_ops */
NULL, /* power */
bge_quiesce /* quiesce */
);
static struct modldrv bge_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
bge_ident, /* short description */
&bge_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
};
int
{
}
int
_init(void)
{
int status;
if (status == DDI_SUCCESS)
else
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS) {
}
return (status);
}
/*
* bge_add_intrs:
*
* Register FIXED or MSI interrupts.
*/
static int
{
/* Get number of interrupts */
return (DDI_FAILURE);
}
/* Get number of available interrupts */
return (DDI_FAILURE);
}
BGE_DEBUG(("%s: nintrs() returned %d, navail returned %d",
}
/*
* BGE hardware generates only single MSI even though it claims
* to support multiple MSIs. So, hard code MSI count value to 1.
*/
if (intr_type == DDI_INTR_TYPE_MSI) {
count = 1;
} else {
}
/* Allocate an array of interrupt handles */
/* Call ddi_intr_alloc() */
return (DDI_FAILURE);
}
BGE_DEBUG(("%s: Requested: %d, Received: %d",
}
/*
* Get priority for first msi, assume remaining are all the same
*/
DDI_SUCCESS) {
/* Free already allocated intr */
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
/* Call ddi_intr_add_handler() */
for (i = 0; i < actual; i++) {
"failed %d\n", ret);
/* Free already allocated intr */
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
}
!= DDI_SUCCESS) {
for (i = 0; i < actual; i++) {
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* bge_rem_intrs:
*
* Unregister FIXED or MSI interrupts
*/
static void
{
int i;
/* Call ddi_intr_remove_handler() */
}
}
void
{
int i;
/* Call ddi_intr_block_enable() for MSI interrupts */
} else {
/* Call ddi_intr_enable for MSI or FIXED interrupts */
}
}
}
void
{
int i;
/* Call ddi_intr_block_disable() */
} else {
}
}
}
int
{
int status = 0;
}
#ifdef BGE_IPMI_ASF
#else
#endif
}
return (status);
}