/*
* 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 (c) 2012 Gary Mills
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2009, Pyun YongHyeon <yongari@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/ethernet.h>
#include <sys/sysmacros.h>
#include <sys/dditypes.h>
#include <sys/byteorder.h>
#include "atge.h"
#include "atge_l1c_reg.h"
#include "atge_cmn_reg.h"
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1C_TX_RING_ALIGN, /* dma_attr_align */
0x0000fffc, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0x0000ffffffffull, /* dma_attr_maxxfer */
0x0000ffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1C_RX_RING_ALIGN, /* dma_attr_align */
0x0000fffc, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0x0000ffffffffull, /* dma_attr_maxxfer */
0x0000ffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1C_CMB_ALIGN, /* dma_attr_align */
0x0000fffc, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0x0000ffffffffull, /* dma_attr_maxxfer */
0x0000ffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1C_SMB_ALIGN, /* dma_attr_align */
0x0000fffc, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0x0000ffffffffull, /* dma_attr_maxxfer */
0x0000ffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1C_RR_RING_ALIGN, /* dma_attr_align */
0x0000fffc, /* dma_attr_burstsizes */
1, /* dma_attr_minxfer */
0x0000ffffffffull, /* dma_attr_maxxfer */
0x0000ffffffffull, /* dma_attr_seg */
1, /* dma_attr_sgllen */
1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
int
{
int err;
/*
* Allocate TX ring descriptor.
*/
" desc ring");
return (DDI_FAILURE);
}
/*
* Allocate DMA buffers for TX ring.
*/
if (err != DDI_SUCCESS) {
" TX Ring");
return (err);
}
/*
* Allocate RX ring.
*/
" for RX Ring");
return (DDI_FAILURE);
}
/*
* Allocate DMA buffers for RX ring.
*/
if (err != DDI_SUCCESS) {
" RX buffers");
return (err);
}
/*
* Allocate CMB used for fetching interrupt status data.
*/
return (DDI_FAILURE);
}
/*
* RR ring (Return Ring for RX and TX).
*/
" for RX RR ring");
return (DDI_FAILURE);
}
/*
* SMB for statistics.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
/*
* Free TX ring.
*/
}
}
}
}
}
/*
* Free RX ring.
*/
}
}
/*
* Free the memory allocated for gathering hw stats.
*/
}
/*
* Free the private area.
*/
}
}
void
{
int i;
for (i = 0; i < L1C_RX_RING_CNT; i++) {
(i * sizeof (l1c_rx_desc_t)));
/* No length field. */
}
/* Let controller know availability of new Rx buffers. */
}
void
{
}
void
{
l1c->atge_l1c_rr_consumers = 0;
}
void
{
}
void
{
}
void
{
atge_ring_t *r;
/*
* Clear WOL status and disable all WOL feature as WOL
* would interfere Rx operation under normal environments.
*/
/* TX */
r = atgep->atge_tx_ring;
/* We don't use high priority ring. */
/* RX */
r = l1c->atge_rx_ring;
/* We use one Rx ring. */
/* RR Ring */
/*
* Let hardware split jumbo frames into alc_max_buf_sized chunks.
* if it do not fit the buffer size. Rx return descriptor holds
* a counter that indicates how many fragments were made by the
* hardware. The buffer size should be multiple of 8 bytes.
* Since hardware has limit on the size of buffer size, always
* use the maximum value.
* For strict-alignment architectures make sure to reduce buffer
* size by 8 bytes to make room for alignment fixup.
*/
/* Set Rx return descriptor base addresses. */
/* We use one Rx return ring. */
/* CMB */
/* SMB */
/*
* Set RX return ring (RR) counter.
*/
/* Set Rx descriptor counter. */
/* Set Rx return descriptor counter. */
/*
* Set TX descriptor counter.
*/
/* Reconfigure SRAM - Vendor magic. */
break;
}
/*
* Inform hardware that we have loaded DMA registers.
*/
/* Configure interrupt moderation timer. */
/*
* We don't want to automatic interrupt clear as task queue
* for the interrupt should know interrupt status.
*/
reg = 0;
}
void
{
int i;
/*
* Clear RX stats first.
*/
i = 0;
i += sizeof (uint32_t);
}
/*
* Clear TX stats.
*/
i = 0;
i += sizeof (uint32_t);
}
}
void
{
/* Rx stats. */
/* Tx stats. */
/*
* Update global counters in atge_t.
*/
/*
* tx_pkts_truncated counter looks suspicious. It constantly
* increments with no sign of Tx errors. Hence we don't factor it.
*/
}
void
{
int t;
if ((reg & ATGE_CFG_TX_ENB) != 0) {
reg &= ~ATGE_CFG_TX_ENB;
}
/* Stop TX DMA engine. */
if ((reg & DMA_CFG_RD_ENB) != 0) {
reg &= ~DMA_CFG_RD_ENB;
}
for (t = ATGE_RESET_TIMEOUT; t > 0; t--) {
(IDLE_STATUS_TXMAC | IDLE_STATUS_DMARD)) == 0)
break;
drv_usecwait(10);
}
if (t == 0) {
/* This should be an FMA event. */
}
}
void
{
int t;
if ((reg & ATGE_CFG_RX_ENB) != 0) {
reg &= ~ATGE_CFG_RX_ENB;
}
/* Stop RX DMA engine. */
if ((reg & DMA_CFG_WR_ENB) != 0) {
reg &= ~DMA_CFG_WR_ENB;
}
for (t = ATGE_RESET_TIMEOUT; t > 0; t--) {
(IDLE_STATUS_RXMAC | IDLE_STATUS_DMAWR)) == 0)
break;
drv_usecwait(10);
}
if (t == 0) {
/* This should be an FMA event. */
}
}
/*
* Receives (consumes) packets.
*/
static mblk_t *
{
int sync = 0;
for (;;) {
ATGE_DB(("%s: %s() PKT -- rdinfo : 0x%x,"
"status : 0x%x, totlen : %d,"
if ((status & L1C_RRD_VALID) == 0) {
break;
}
L1C_RRD_ERR_ICMP | L1C_RRD_ERR_LENGTH)) != 0) {
break;
}
atgep->atge_ipackets++;
/*
* If there are more than one segments, then the first
* segment should be of size MTU. We couldn't verify
* this as our driver does not support changing MTU
* or Jumbo Frames.
*/
if (nsegs > 1) {
} else {
}
} else {
ATGE_DB(("%s: %s() PKT mp == NULL totlen : %d",
atgep->atge_norcvbuf++;
}
break;
}
ATGE_DB(("%s: %s() len : %d, rxcons : %d, pktlen : %d",
pktlen));
}
} else {
}
} else {
}
/*
* Tell the chip that this RR can be reused.
*/
sync++;
}
if (sync) {
/*
* Let controller know availability of new Rx buffers.
*/
ATGE_DB(("%s: %s() PKT Recved -> r_consumer : %d, rx_cons : %d"
" atge_l1c_rr_consumers : %d",
}
return (rx_head);
}
/*
* The interrupt handler for L1C chip.
*/
/*ARGSUSED*/
{
int resched = 0;
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
return (DDI_INTR_CLAIMED);
}
ATGE_DB(("%s: %s() entry status : %x",
/*
* Disable interrupts.
*/
if (status & L1C_INTR_GPHY) {
/* clear PHY interrupt source before we ack interrupts */
(void) atge_mii_read(atgep,
}
/*
* Check if chip is running, only then do the work.
*/
ATGE_DB(("%s: %s() atge_l1c_intr_status : %x, "
"atge_l1c_rx_prod_cons : %d, atge_l1c_tx_prod_cons : %d"
" atge_l1c_rr_consumers : %d",
if (status & L1C_INTR_SMB)
/*
* Check for errors.
*/
if (status & (L1C_INTR_DMA_RD_TO_RST |
/* This should be an FMA event. */
"L1C chip detected a fatal error, "
"interrupt status: %x", status);
if (status & L1C_INTR_DMA_RD_TO_RST) {
"DMA read error");
}
if (status & L1C_INTR_DMA_WR_TO_RST) {
"DMA write error");
}
if (status & L1C_INTR_TXQ_TO_RST) {
"Transmit queue error");
}
/* This should be an FMA event. */
/*
* Device has failed fatally.
* It will not be restarted by the driver.
*/
goto done;
}
if (status & L1C_INTR_TX_PKT) {
int cons;
if (atgep->atge_tx_resched) {
atgep->atge_tx_resched = 0;
resched = 1;
}
}
}
/* Re-enable interrupts. */
done:
if (status & L1C_INTR_GPHY) {
/* link down */
ATGE_DB(("%s: %s() MII_CHECK Performed",
}
/*
* Pass the list of packets received from chip to MAC layer.
*/
if (rx_head) {
}
/*
* Let MAC start sending pkts if the downstream was asked to pause.
*/
if (resched)
return (DDI_INTR_CLAIMED);
}
void
{
/* Sync descriptors. */
/* Kick. Assume we're using normal Tx priority queue. */
}