atge_l1e.c revision 0eb090a7674ebcdcb1c35501097edeb5f2395459
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/sysmacros.h>
#include <sys/dditypes.h>
#include <sys/byteorder.h>
#include "atge.h"
#include "atge_l1e_reg.h"
#include "atge_cmn_reg.h"
/*
* L1E specfic functions.
*/
void atge_l1e_device_reset(atge_t *);
void atge_l1e_stop_rx_mac(atge_t *);
void atge_l1e_stop_tx_mac(atge_t *);
static ddi_dma_attr_t atge_l1e_dma_attr_tx_desc = {
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1E_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 */
};
static ddi_dma_attr_t atge_l1e_dma_attr_rx_desc = {
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1E_RX_PAGE_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 */
};
static ddi_dma_attr_t atge_l1e_dma_attr_cmb = {
DMA_ATTR_V0, /* dma_attr_version */
0, /* dma_attr_addr_lo */
0x0000ffffffffull, /* dma_attr_addr_hi */
0x0000ffffffffull, /* dma_attr_count_max */
L1E_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 */
};
void
{
int pages;
return;
return;
}
}
}
int
{
int err;
int pages;
int guard_size;
/*
* Allocate TX ring descriptor.
*/
ATGE_DB(("%s :%s failed",
return (DDI_FAILURE);
}
/*
* Allocate DMA buffers for TX ring.
*/
if (err != DDI_SUCCESS) {
ATGE_DB(("%s :%s() TX buffers failed",
return (err);
}
/*
* Allocate RX pages.
*/
else
ATGE_DB(("%s: %s() atge_l1e_pagesize : %d, L1E_RX_PAGE_SZ : %d",
err = DDI_SUCCESS;
err = DDI_FAILURE;
break;
}
}
if (err == DDI_FAILURE) {
ATGE_DB(("%s :%s RX pages failed",
return (DDI_FAILURE);
}
/*
* Allocate CMB used for fetching interrupt status data.
*/
err = DDI_SUCCESS;
ATGE_DB(("%s :%s() RX CMB failed",
return (DDI_FAILURE);
}
if (err == DDI_FAILURE) {
ATGE_DB(("%s :%s() RX CMB failed",
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
/*
* Free TX ring.
*/
}
}
return;
/*
* Free RX CMB.
*/
}
/*
* Free RX buffers and RX ring.
*/
/*
* Free the memory allocated for gathering hw stats.
*/
}
}
void
{
int pages;
l1e->atge_l1e_rx_curp = 0;
l1e->atge_l1e_rx_seqno = 0;
l1e->atge_l1e_rx_page_cons = 0;
}
}
void
{
}
void
{
/*
* Clear WOL status and disable all WOL feature as WOL
* would interfere Rx operation under normal environments.
*/
/*
* Set Tx descriptor/RXF0/CMB base addresses. They share
* the same high address part of DMAable region.
*/
/* Set Rx page base address, note we use single queue. */
/* Mark RXF0 valid. */
/* Set Rx page size, excluding guard frame size. */
/* Tell hardware that we're ready to load DMA blocks. */
(4 << INT_TRIG_TX_THRESH_SHIFT));
/*
* Set interrupt trigger timer, its purpose and relation
* with interrupt moderation mechanism is not clear yet.
*/
}
mblk_t *
{
int prog;
/* Sync CMB first */
/*
* Get the producer offset from CMB.
*/
/* Sync current RX Page as well */
ATGE_DB(("%s: %s() prod : %d, cons : %d, curr page : %d, gen : (%d)"
" cmb[0,1] : %d, %d",
break;
/*
* We have not seen this happening but we
* must restart the chip if that happens.
*/
ATGE_DB(("%s: %s() MISS-MATCH in seqno :%d,"
" atge_l1e_rx_seqno : %d, length : %d, flags : %x",
/*
* Return all the pkts received before restarting
* the chip.
*/
return (rx_head);
} else {
l1e->atge_l1e_rx_seqno++;
}
/*
* We will pass the pkt to upper layer provided it's clear
* from any error.
*/
if ((flags & L1E_RD_ERROR) != 0) {
L1E_RD_TRUNC)) != 0) {
ATGE_DB(("%s: %s() ERRORED PKT : %x",
atgep->atge_errrcv++;
continue;
}
}
/*
*/
ATGE_DB(("%s: %s() PKT len > error : %d",
continue;
}
else {
}
atgep->atge_ipackets++;
} else {
ATGE_DB(("%s: %s() PKT mp == NULL len : %d",
atgep->atge_norcvbuf++;
}
}
ATGE_DB(("%s: %s() seqno :%d, atge_l1e_rx_seqno :"
" %d, length : %d,"
" flags : %x, cons : %d, prod : %d",
}
ATGE_DB(("%s: %s() receive completed (gen : %d) : cons : %d,"
" prod :%d, L1E_RX_PAGE_SZ : %d (prog:%d)",
L1E_RX_PAGE_SZ, prog));
gen++;
return (rx_head);
}
void
{
uint32_t *p;
/*
* Update consumer position.
*/
/*
* If we need to flip to the other page. Note that we use only two
* pages.
*/
ATGE_DB(("%s: %s() cons : %d, prod :%d, L1E_RX_PAGE_SZ : %d",
/*
* Clear the producer.
*/
p = (void *)dma_rx_cmb->addr;
p = p + curr;
*p = 0;
/*
* Notify the NIC that the current RX page is available again.
*/
/*
* End of Rx page reached, let hardware reuse this page.
*/
l1e->atge_l1e_rx_page_cons = 0;
/*
* Switch to alternate Rx page.
*/
curr ^= 1;
/*
* Page flipped, sync CMB and then Rx page.
*/
p = (void *)dma_rx_cmb->addr;
ATGE_DB(("%s: %s() PAGE FLIPPED -> %d, producer[0,1]: %d, %d",
}
}
void
{
/*
* Ask chip to send the packet now.
*/
}
void
{
int i;
/*
* Clear RX stats first.
*/
i = 0;
i += sizeof (uint32_t);
}
/*
* Clear TX stats.
*/
i = 0;
i += sizeof (uint32_t);
}
}
void
{
int i;
/* Read Rx statistics. */
i = 0;
i += sizeof (uint32_t);
}
/* Read Tx statistics. */
i = 0;
i += sizeof (uint32_t);
}
/*
* SMB is cleared everytime we read; hence we always do '+='.
*/
/* 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
{
}
}
/*
*/
/*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.
*/
/*
* Check if chip is running, only then do the work.
*/
}
/*
* Check for errors.
*/
if (status & L1E_INTR_ERRORS) {
"L1E chip found an error intr status : %x",
status);
if (status &
goto done;
}
if (status & INTR_TX_FIFO_UNDERRUN) {
}
}
if (status & INTR_TX_PKT) {
int cons;
if (atgep->atge_tx_resched) {
atgep->atge_tx_resched = 0;
resched = 1;
}
}
}
/*
* Enable interrupts.
*/
done:
/*
* Ack interrupts from PHY
*/
(void) atge_mii_read(atgep,
}
/*
* 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);
}