ch_mac.c revision d39a76e7b087a3d0927cbe6898dc0a6770fa6c68
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (C) 2003-2005 Chelsio Communications. All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI" /* ch_mac.c */
#include "gmac.h"
#include "regs.h"
#include "fpga_defs.h"
#define MAC_CSR_INTERFACE_GMII 0x0
#define MAC_CSR_INTERFACE_TBI 0x1
#define MAC_CSR_INTERFACE_MII 0x2
#define MAC_CSR_INTERFACE_RMII 0x3
/* Chelsio's MAC statistics. */
struct mac_statistics {
/* Transmit */
u32 TxFramesTransmittedOK;
u32 TxReserved1;
u32 TxReserved2;
u32 TxOctetsTransmittedOK;
u32 TxFramesWithDeferredXmissions;
u32 TxLateCollisions;
u32 TxFramesAbortedDueToXSCollisions;
u32 TxFramesLostDueToIntMACXmitError;
u32 TxReserved3;
u32 TxMulticastFrameXmittedOK;
u32 TxBroadcastFramesXmittedOK;
u32 TxFramesWithExcessiveDeferral;
u32 TxPAUSEMACCtrlFramesTransmitted;
/* Receive */
u32 RxFramesReceivedOK;
u32 RxFrameCheckSequenceErrors;
u32 RxAlignmentErrors;
u32 RxOctetsReceivedOK;
u32 RxFramesLostDueToIntMACRcvError;
u32 RxMulticastFramesReceivedOK;
u32 RxBroadcastFramesReceivedOK;
u32 RxInRangeLengthErrors;
u32 RxTxOutOfRangeLengthField;
u32 RxFrameTooLongErrors;
u32 RxPAUSEMACCtrlFramesReceived;
};
static int static_aPorts[] = {
FPGA_GMAC_INTERRUPT_PORT0,
FPGA_GMAC_INTERRUPT_PORT1,
FPGA_GMAC_INTERRUPT_PORT2,
FPGA_GMAC_INTERRUPT_PORT3
};
struct _cmac_instance {
u32 index;
};
static int mac_intr_enable(struct cmac *mac)
{
u32 mac_intr;
if (t1_is_asic(mac->adapter)) {
/* ASIC */
/*EMPTY*/
/* We don't use the on chip MAC for ASIC products. */
} else {
/* FPGA */
/* Set parent gmac interrupt. */
mac_intr = t1_read_reg_4(mac->adapter, A_PL_ENABLE);
mac_intr |= FPGA_PCIX_INTERRUPT_GMAC;
t1_write_reg_4(mac->adapter, A_PL_ENABLE, mac_intr);
mac_intr = t1_read_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
mac_intr |= static_aPorts[mac->instance->index];
t1_write_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_ENABLE, mac_intr);
}
return (0);
}
static int mac_intr_disable(struct cmac *mac)
{
u32 mac_intr;
if (t1_is_asic(mac->adapter)) {
/* ASIC */
/*EMPTY*/
/* We don't use the on chip MAC for ASIC products. */
} else {
/* FPGA */
/* Set parent gmac interrupt. */
mac_intr = t1_read_reg_4(mac->adapter, A_PL_ENABLE);
mac_intr &= ~FPGA_PCIX_INTERRUPT_GMAC;
t1_write_reg_4(mac->adapter, A_PL_ENABLE, mac_intr);
mac_intr = t1_read_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
mac_intr &= ~(static_aPorts[mac->instance->index]);
t1_write_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_ENABLE, mac_intr);
}
return (0);
}
static int mac_intr_clear(struct cmac *mac)
{
u32 mac_intr;
if (t1_is_asic(mac->adapter)) {
/* ASIC */
/*EMPTY*/
/* We don't use the on chip MAC for ASIC products. */
} else {
/* FPGA */
/* Set parent gmac interrupt. */
t1_write_reg_4(mac->adapter, A_PL_CAUSE,
FPGA_PCIX_INTERRUPT_GMAC);
mac_intr = t1_read_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_CAUSE);
mac_intr |= (static_aPorts[mac->instance->index]);
t1_write_reg_4(mac->adapter,
FPGA_GMAC_ADDR_INTERRUPT_CAUSE, mac_intr);
}
return (0);
}
static int mac_get_address(struct cmac *mac, u8 addr[6])
{
u32 data32_lo, data32_hi;
data32_lo = t1_read_reg_4(mac->adapter,
MAC_REG_IDLO(mac->instance->index));
data32_hi = t1_read_reg_4(mac->adapter,
MAC_REG_IDHI(mac->instance->index));
addr[0] = (u8) ((data32_hi >> 8) & 0xFF);
addr[1] = (u8) ((data32_hi) & 0xFF);
addr[2] = (u8) ((data32_lo >> 24) & 0xFF);
addr[3] = (u8) ((data32_lo >> 16) & 0xFF);
addr[4] = (u8) ((data32_lo >> 8) & 0xFF);
addr[5] = (u8) ((data32_lo) & 0xFF);
return (0);
}
static int mac_reset(struct cmac *mac)
{
u32 data32;
int mac_in_reset, time_out = 100;
int idx = mac->instance->index;
data32 = t1_read_reg_4(mac->adapter, MAC_REG_CSR(idx));
t1_write_reg_4(mac->adapter, MAC_REG_CSR(idx),
data32 | F_MAC_RESET);
do {
data32 = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(idx));
mac_in_reset = data32 & F_MAC_RESET;
if (mac_in_reset)
DELAY_US(1);
} while (mac_in_reset && --time_out);
if (mac_in_reset) {
CH_ERR("%s: MAC %d reset timed out\n",
adapter_name(mac->adapter), idx);
return (2);
}
return (0);
}
static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm)
{
u32 val;
val = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index));
val &= ~(F_MAC_PROMISC | F_MAC_MC_ENABLE);
val |= V_MAC_PROMISC(t1_rx_mode_promisc(rm) != 0);
val |= V_MAC_MC_ENABLE(t1_rx_mode_allmulti(rm) != 0);
t1_write_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index), val);
return (0);
}
static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex,
int fc)
{
u32 data32;
data32 = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index));
data32 &= ~(F_MAC_HALF_DUPLEX | V_MAC_SPEED(M_MAC_SPEED) |
V_INTERFACE(M_INTERFACE) | F_MAC_TX_PAUSE_ENABLE |
F_MAC_RX_PAUSE_ENABLE);
switch (speed) {
case SPEED_10:
case SPEED_100:
data32 |= V_INTERFACE(MAC_CSR_INTERFACE_MII);
data32 |= V_MAC_SPEED(speed == SPEED_10 ? 0 : 1);
break;
case SPEED_1000:
data32 |= V_INTERFACE(MAC_CSR_INTERFACE_GMII);
data32 |= V_MAC_SPEED(2);
break;
}
if (duplex >= 0)
data32 |= V_MAC_HALF_DUPLEX(duplex == DUPLEX_HALF);
if (fc >= 0) {
data32 |= V_MAC_RX_PAUSE_ENABLE((fc & PAUSE_RX) != 0);
data32 |= V_MAC_TX_PAUSE_ENABLE((fc & PAUSE_TX) != 0);
}
t1_write_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index), data32);
return (0);
}
static int mac_enable(struct cmac *mac, int which)
{
u32 val;
val = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index));
if (which & MAC_DIRECTION_RX)
val |= F_MAC_RX_ENABLE;
if (which & MAC_DIRECTION_TX)
val |= F_MAC_TX_ENABLE;
t1_write_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index), val);
return (0);
}
static int mac_disable(struct cmac *mac, int which)
{
u32 val;
val = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index));
if (which & MAC_DIRECTION_RX)
val &= ~F_MAC_RX_ENABLE;
if (which & MAC_DIRECTION_TX)
val &= ~F_MAC_TX_ENABLE;
t1_write_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index), val);
return (0);
}
int
mac_set_ifs(struct cmac *mac, u32 mode)
{
t1_write_reg_4(mac->adapter,
MAC_REG_IFS(mac->instance->index), mode);
return (0);
}
int
mac_enable_isl(struct cmac *mac)
{
u32 data32 = t1_read_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index));
data32 |= F_MAC_RX_ENABLE | F_MAC_TX_ENABLE;
t1_write_reg_4(mac->adapter,
MAC_REG_CSR(mac->instance->index), data32);
return (0);
}
static int mac_set_mtu(struct cmac *mac, int mtu)
{
if (mtu > 9600)
return (-EINVAL);
t1_write_reg_4(mac->adapter,
MAC_REG_LARGEFRAMELENGTH(mac->instance->index),
mtu + 14 + 4);
return (0);
}
/* ARGSUSED */
static const struct cmac_statistics *mac_update_statistics(struct cmac *mac,
int flag)
{
struct mac_statistics st;
u32 *p = (u32 *) & st, i;
t1_write_reg_4(mac->adapter,
MAC_REG_RMCNT(mac->instance->index), 0);
for (i = 0; i < sizeof (st) / sizeof (u32); i++)
*p++ = t1_read_reg_4(mac->adapter,
MAC_REG_RMDATA(mac->instance->index));
/* XXX convert stats */
return (&mac->stats);
}
static void mac_destroy(struct cmac *mac)
{
t1_os_free((void *)mac, sizeof (*mac) + sizeof (cmac_instance));
}
#ifdef C99_NOT_SUPPORTED
static struct cmac_ops chelsio_mac_ops = {
mac_destroy,
mac_reset,
mac_intr_enable,
mac_intr_disable,
mac_intr_clear,
NULL,
mac_enable,
mac_disable,
NULL,
NULL,
mac_set_mtu,
mac_set_rx_mode,
mac_set_speed_duplex_fc,
NULL,
mac_update_statistics,
mac_get_address,
NULL
};
#else
static struct cmac_ops chelsio_mac_ops = {
.destroy = mac_destroy,
.reset = mac_reset,
.interrupt_enable = mac_intr_enable,
.interrupt_disable = mac_intr_disable,
.interrupt_clear = mac_intr_clear,
.enable = mac_enable,
.disable = mac_disable,
.set_mtu = mac_set_mtu,
.set_rx_mode = mac_set_rx_mode,
.set_speed_duplex_fc = mac_set_speed_duplex_fc,
.macaddress_get = mac_get_address,
.statistics_update = mac_update_statistics,
};
#endif
static struct cmac *mac_create(adapter_t *adapter, int index)
{
struct cmac *mac;
u32 data32;
if (index >= 4)
return (NULL);
mac = t1_os_malloc_wait_zero(sizeof (*mac) + sizeof (cmac_instance));
if (!mac)
return (NULL);
mac->ops = &chelsio_mac_ops;
mac->instance = (cmac_instance *) (mac + 1);
mac->instance->index = index;
mac->adapter = adapter;
data32 = t1_read_reg_4(adapter, MAC_REG_CSR(mac->instance->index));
data32 &= ~(F_MAC_RESET | F_MAC_PROMISC | F_MAC_PROMISC |
F_MAC_LB_ENABLE | F_MAC_RX_ENABLE | F_MAC_TX_ENABLE);
data32 |= F_MAC_JUMBO_ENABLE;
t1_write_reg_4(adapter, MAC_REG_CSR(mac->instance->index), data32);
/* Initialize the random backoff seed. */
data32 = 0x55aa + (3 * index);
t1_write_reg_4(adapter,
MAC_REG_GMRANDBACKOFFSEED(mac->instance->index), data32);
/* Check to see if the mac address needs to be set manually. */
data32 = t1_read_reg_4(adapter, MAC_REG_IDLO(mac->instance->index));
if (data32 == 0 || data32 == 0xffffffff) {
/*
* Add a default MAC address if we can't read one.
*/
t1_write_reg_4(adapter, MAC_REG_IDLO(mac->instance->index),
0x43FFFFFF - index);
t1_write_reg_4(adapter, MAC_REG_IDHI(mac->instance->index),
0x0007);
}
(void) mac_set_mtu(mac, 1500);
return (mac);
}
struct gmac t1_chelsio_mac_ops = {
0,
mac_create
};