niu.c revision 93833965647072e8fb234b6f0cd4060544c6dc4a
/*
* 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 2008 NetXen, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/dlpi.h>
#include <sys/strsun.h>
#include <sys/ethernet.h>
#include <sys/modctl.h>
#include <sys/errno.h>
#include <sys/dditypes.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h>
#include <sys/pci.h>
#include "unm_inc.h"
#include "unm_nic.h"
static long phy_lock_timeout = 100000000;
static int phy_lock(struct unm_adapter_s *adapter)
{
u32 done = 0;
int timeout = 0;
while (!done) {
/* acquire semaphore3 from PCI HW block */
adapter->unm_nic_pci_read_immediate(adapter,
UNM_PCIE_REG(PCIE_SEM3_LOCK), &done);
if (done == 1)
break;
if (timeout >= phy_lock_timeout)
return (-1);
timeout++;
}
adapter->unm_crb_writelit_adapter(adapter, UNM_PHY_LOCK_ID,
PHY_LOCK_DRIVER);
return (0);
}
static void
phy_unlock(struct unm_adapter_s *adapter)
{
u32 val;
/* release semaphore3 */
adapter->unm_nic_pci_read_immediate(adapter,
UNM_PCIE_REG(PCIE_SEM3_UNLOCK), &val);
}
/*
* unm_niu_gbe_phy_read - read a register from the GbE PHY via
* mii management interface.
*
* Note: The MII management interface goes through port 0.
* Individual phys are addressed as follows:
* [15:8] phy id
* [7:0] register number
*
* Returns: 0 success
* -1 error
*
*/
long
unm_niu_gbe_phy_read(struct unm_adapter_s *adapter, long reg,
unm_crbword_t *readval)
{
long phy = adapter->physical_port;
unm_niu_gb_mii_mgmt_address_t address;
unm_niu_gb_mii_mgmt_command_t command;
unm_niu_gb_mii_mgmt_indicators_t status;
long timeout = 0;
long result = 0;
long restore = 0;
unm_niu_gb_mac_config_0_t mac_cfg0;
if (phy_lock(adapter) != 0)
return (-1);
/*
* MII mgmt all goes through port 0 MAC interface, so it cannot be
* in reset
*/
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(0),
&mac_cfg0, 4);
if (mac_cfg0.soft_reset) {
unm_niu_gb_mac_config_0_t temp;
*(unm_crbword_t *)&temp = 0;
temp.tx_reset_pb = 1;
temp.rx_reset_pb = 1;
temp.tx_reset_mac = 1;
temp.rx_reset_mac = 1;
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_GB_MAC_CONFIG_0(0), &temp, 4);
restore = 1;
}
*(unm_crbword_t *)&address = 0;
address.reg_addr = (unm_crbword_t)reg;
address.phy_addr = (unm_crbword_t)phy;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MII_MGMT_ADDR(0),
&address, 4);
*(unm_crbword_t *)&command = 0; /* turn off any prior activity */
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MII_MGMT_COMMAND(0),
&command, 4);
/* send read command */
command.read_cycle = 1;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MII_MGMT_COMMAND(0),
&command, 4);
*(unm_crbword_t *)&status = 0;
do {
adapter->unm_nic_hw_read_wx(adapter,
UNM_NIU_GB_MII_MGMT_INDICATE(0), &status, 4);
timeout++;
} while ((status.busy || status.notvalid) &&
(timeout++ < UNM_NIU_PHY_WAITMAX));
if (timeout < UNM_NIU_PHY_WAITMAX) {
adapter->unm_nic_hw_read_wx(adapter,
UNM_NIU_GB_MII_MGMT_STATUS(0), readval, 4);
result = 0;
} else
result = -1;
if (restore)
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_GB_MAC_CONFIG_0(0), &mac_cfg0, 4);
phy_unlock(adapter);
return (result);
}
/*
* Return the current station MAC address.
* Note that the passed-in value must already be in network byte order.
*/
int
unm_niu_macaddr_get(struct unm_adapter_s *adapter, unsigned char *addr)
{
__uint64_t result;
int phy = adapter->physical_port;
if (addr == NULL)
return (-1);
if ((phy < 0) || (phy > 3))
return (-1);
UNM_WRITE_LOCK_IRQS(&adapter->adapter_lock, flags);
if (adapter->curr_window != 0) {
adapter->unm_nic_pci_change_crbwindow(adapter, 0);
}
result = UNM_NIC_PCI_READ_32((void *)pci_base_offset(adapter,
UNM_NIU_GB_STATION_ADDR_1(phy))) >> 16;
result |= ((uint64_t)UNM_NIC_PCI_READ_32((void *)pci_base_offset(
adapter, UNM_NIU_GB_STATION_ADDR_0(phy)))) << 16;
(void) memcpy(addr, &result, sizeof (unm_ethernet_macaddr_t));
adapter->unm_nic_pci_change_crbwindow(adapter, 1);
UNM_WRITE_UNLOCK_IRQR(&adapter->adapter_lock, flags);
return (0);
}
/*
* Set the station MAC address.
* Note that the passed-in value must already be in network byte order.
*/
int
unm_niu_macaddr_set(struct unm_adapter_s *adapter, unm_ethernet_macaddr_t addr)
{
unm_crbword_t temp = 0;
int phy = adapter->physical_port;
if ((phy < 0) || (phy > 3))
return (-1);
(void) memcpy(&temp, addr, 2);
temp <<= 16;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_STATION_ADDR_1(phy),
&temp, 4);
temp = 0;
(void) memcpy(&temp, ((__uint8_t *)addr)+2, sizeof (unm_crbword_t));
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_STATION_ADDR_0(phy),
&temp, 4);
return (0);
}
/* Enable a GbE interface */
/* ARGSUSED */
native_t unm_niu_enable_gbe_port(struct unm_adapter_s *adapter,
unm_niu_gbe_ifmode_t mode_dont_care)
{
unm_niu_gb_mac_config_0_t mac_cfg0;
unm_niu_gb_mac_config_1_t mac_cfg1;
unm_niu_gb_mii_mgmt_config_t mii_cfg;
native_t port = adapter->physical_port;
int zero = 0;
int one = 1;
u32 port_mode = 0;
mode_dont_care = 0;
if ((port < 0) || (port > UNM_NIU_MAX_GBE_PORTS)) {
return (-1);
}
if (adapter->link_speed != MBPS_10 &&
adapter->link_speed != MBPS_100 &&
adapter->link_speed != MBPS_1000) {
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
/*
* Do NOT fail this call because the cable is unplugged.
* Updated when the link comes up...
*/
adapter->link_speed = MBPS_1000;
} else {
return (-1);
}
}
port_mode = adapter->unm_nic_pci_read_normalize(adapter,
UNM_PORT_MODE_ADDR);
if (port_mode == UNM_PORT_MODE_802_3_AP) {
*(unm_crbword_t *)&mac_cfg0 = 0x0000003f;
*(unm_crbword_t *)&mac_cfg1 = 0x0000f2df;
unm_crb_write_adapter(UNM_NIU_AP_MAC_CONFIG_0(port), &mac_cfg0,
adapter);
unm_crb_write_adapter(UNM_NIU_AP_MAC_CONFIG_1(port), &mac_cfg1,
adapter);
} else {
*(unm_crbword_t *)&mac_cfg0 = 0;
mac_cfg0.soft_reset = 1;
unm_crb_write_adapter(UNM_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0,
adapter);
*(unm_crbword_t *)&mac_cfg0 = 0;
mac_cfg0.tx_enable = 1;
mac_cfg0.rx_enable = 1;
mac_cfg0.rx_flowctl = 0;
mac_cfg0.tx_reset_pb = 1;
mac_cfg0.rx_reset_pb = 1;
mac_cfg0.tx_reset_mac = 1;
mac_cfg0.rx_reset_mac = 1;
unm_crb_write_adapter(UNM_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0,
adapter);
*(unm_crbword_t *)&mac_cfg1 = 0;
mac_cfg1.preamblelen = 0xf;
mac_cfg1.duplex = 1;
mac_cfg1.crc_enable = 1;
mac_cfg1.padshort = 1;
mac_cfg1.checklength = 1;
mac_cfg1.hugeframes = 1;
switch (adapter->link_speed) {
case MBPS_10:
case MBPS_100: /* Fall Through */
mac_cfg1.intfmode = 1;
unm_crb_write_adapter(UNM_NIU_GB_MAC_CONFIG_1
(port), &mac_cfg1, adapter);
/* set mii mode */
unm_crb_write_adapter(
UNM_NIU_GB0_GMII_MODE+(port<<3),
&zero, adapter);
unm_crb_write_adapter(
UNM_NIU_GB0_MII_MODE+(port<< 3),
&one, adapter);
break;
case MBPS_1000:
mac_cfg1.intfmode = 2;
unm_crb_write_adapter(
UNM_NIU_GB_MAC_CONFIG_1(port),
&mac_cfg1, adapter);
/* set gmii mode */
unm_crb_write_adapter(
UNM_NIU_GB0_MII_MODE+(port << 3),
&zero, adapter);
unm_crb_write_adapter(
UNM_NIU_GB0_GMII_MODE+(port << 3),
&one, adapter);
break;
default:
/* Will not happen */
break;
}
*(unm_crbword_t *)&mii_cfg = 0;
mii_cfg.clockselect = 7;
unm_crb_write_adapter(UNM_NIU_GB_MII_MGMT_CONFIG(port),
&mii_cfg, adapter);
*(unm_crbword_t *)&mac_cfg0 = 0;
mac_cfg0.tx_enable = 1;
mac_cfg0.rx_enable = 1;
mac_cfg0.tx_flowctl = 0;
mac_cfg0.rx_flowctl = 0;
unm_crb_write_adapter(UNM_NIU_GB_MAC_CONFIG_0(port),
&mac_cfg0, adapter);
}
return (0);
}
/* Disable a GbE interface */
native_t
unm_niu_disable_gbe_port(struct unm_adapter_s *adapter)
{
native_t port = adapter->physical_port;
unm_niu_gb_mac_config_0_t mac_cfg0;
if ((port < 0) || (port > UNM_NIU_MAX_GBE_PORTS))
return (-1);
*(unm_crbword_t *)&mac_cfg0 = 0;
mac_cfg0.soft_reset = 1;
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 0);
else
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 4);
return (0);
}
/* Disable an XG interface */
native_t
unm_niu_disable_xg_port(struct unm_adapter_s *adapter)
{
native_t port = adapter->physical_port;
unm_niu_xg_mac_config_0_t mac_cfg;
*(unm_crbword_t *)&mac_cfg = 0;
mac_cfg.soft_reset = 1;
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
if (port != 0)
return (-1);
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_XGE_CONFIG_0,
&mac_cfg, 4);
} else {
if ((port < 0) || (port >= UNM_NIU_MAX_XG_PORTS))
return (-1);
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_XGE_CONFIG_0 +
(port * 0x10000), &mac_cfg, 4);
}
return (0);
}
/* Set promiscuous mode for a GbE interface */
native_t
unm_niu_set_promiscuous_mode(struct unm_adapter_s *adapter,
unm_niu_prom_mode_t mode)
{
native_t port = adapter->physical_port;
unm_niu_gb_drop_crc_t reg;
unm_niu_gb_mac_config_0_t mac_cfg;
unm_crbword_t data;
int cnt = 0, ret = 0;
ulong_t val;
if ((port < 0) || (port > UNM_NIU_MAX_GBE_PORTS))
return (-1);
/* Turn off mac */
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(port),
&mac_cfg, 4);
mac_cfg.rx_enable = 0;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(port),
&mac_cfg, 4);
/* wait until mac is drained by sre */
/* Port 0 rx fifo bit 5 */
val = (0x20 << port);
adapter->unm_crb_writelit_adapter(adapter, UNM_NIU_FRAME_COUNT_SELECT,
val);
do {
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_FRAME_COUNT,
&val, 4);
cnt++;
if (cnt > 2000) {
ret = -1;
break;
}
drv_usecwait(10);
} while (val);
/* now set promiscuous mode */
if (ret != -1) {
if (mode == UNM_NIU_PROMISCOUS_MODE)
data = 0;
else
data = 1;
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_GB_DROP_WRONGADDR,
&reg, 4);
switch (port) {
case 0:
reg.drop_gb0 = data;
break;
case 1:
reg.drop_gb1 = data;
break;
case 2:
reg.drop_gb2 = data;
break;
case 3:
reg.drop_gb3 = data;
break;
default:
ret = -1;
break;
}
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_DROP_WRONGADDR,
&reg, 4);
}
/* turn the mac on back */
mac_cfg.rx_enable = 1;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(port),
&mac_cfg, 4);
return (ret);
}
/*
* Set the MAC address for an XG port
* Note that the passed-in value must already be in network byte order.
*/
int
unm_niu_xg_macaddr_set(struct unm_adapter_s *adapter,
unm_ethernet_macaddr_t addr)
{
int phy = adapter->physical_port;
unm_crbword_t temp = 0;
u32 port_mode = 0;
if ((phy < 0) || (phy > 3))
return (-1);
switch (phy) {
case 0:
(void) memcpy(&temp, addr, 2);
temp <<= 16;
port_mode = adapter->unm_nic_pci_read_normalize(adapter,
UNM_PORT_MODE_ADDR);
if (port_mode == UNM_PORT_MODE_802_3_AP) {
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_AP_STATION_ADDR_1(phy), &temp, 4);
temp = 0;
(void) memcpy(&temp, ((__uint8_t *)addr) + 2,
sizeof (unm_crbword_t));
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_AP_STATION_ADDR_0(phy), &temp, 4);
} else {
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_XGE_STATION_ADDR_0_1, &temp, 4);
temp = 0;
(void) memcpy(&temp, ((__uint8_t *)addr) + 2,
sizeof (unm_crbword_t));
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_XGE_STATION_ADDR_0_HI, &temp, 4);
}
break;
case 1:
(void) memcpy(&temp, addr, 2);
temp <<= 16;
port_mode = adapter->unm_nic_pci_read_normalize(adapter,
UNM_PORT_MODE_ADDR);
if (port_mode == UNM_PORT_MODE_802_3_AP) {
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_AP_STATION_ADDR_1(phy), &temp, 4);
temp = 0;
(void) memcpy(&temp, ((__uint8_t *)addr) + 2,
sizeof (unm_crbword_t));
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_AP_STATION_ADDR_0(phy), &temp, 4);
} else {
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_XGE_STATION_ADDR_0_1, &temp, 4);
temp = 0;
(void) memcpy(&temp, ((__uint8_t *)addr) + 2,
sizeof (unm_crbword_t));
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_XGE_STATION_ADDR_0_HI, &temp, 4);
}
break;
default:
cmn_err(CE_WARN, "Unknown port %d\n", phy);
return (DDI_FAILURE);
}
return (0);
}
native_t
unm_niu_xg_set_promiscuous_mode(struct unm_adapter_s *adapter,
unm_niu_prom_mode_t mode)
{
long reg;
unm_niu_xg_mac_config_0_t mac_cfg;
native_t port = adapter->physical_port;
int cnt = 0;
int result = 0;
u32 port_mode = 0;
if ((port < 0) || (port > UNM_NIU_MAX_XG_PORTS))
return (-1);
port_mode = adapter->unm_nic_pci_read_normalize(adapter,
UNM_PORT_MODE_ADDR);
if (port_mode == UNM_PORT_MODE_802_3_AP) {
reg = 0;
adapter->unm_nic_hw_write_wx(adapter,
UNM_NIU_GB_DROP_WRONGADDR, (void*)&reg, 4);
} else {
/* Turn off mac */
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_XGE_CONFIG_0 +
(0x10000 * port), &mac_cfg, 4);
mac_cfg.rx_enable = 0;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_XGE_CONFIG_0 +
(0x10000 * port), &mac_cfg, 4);
/* wait until mac is drained by sre */
if ((adapter->ahw.boardcfg.board_type !=
UNM_BRDTYPE_P2_SB31_10G_IMEZ) &&
(adapter->ahw.boardcfg.board_type !=
UNM_BRDTYPE_P2_SB31_10G_HMEZ)) {
/* single port case bit 9 */
reg = 0x0200;
adapter->unm_crb_writelit_adapter(adapter,
UNM_NIU_FRAME_COUNT_SELECT, reg);
} else {
/* Port 0 rx fifo bit 5 */
reg = (0x20 << port);
adapter->unm_crb_writelit_adapter(adapter,
UNM_NIU_FRAME_COUNT_SELECT, reg);
}
do {
adapter->unm_nic_hw_read_wx(adapter,
UNM_NIU_FRAME_COUNT, &reg, 4);
cnt++;
if (cnt > 2000) {
result = -1;
break;
}
drv_usecwait(10);
} while (reg);
/* now set promiscuous mode */
if (result != -1) {
adapter->unm_nic_hw_read_wx(adapter,
UNM_NIU_XGE_CONFIG_1 + (0x10000 * port), &reg, 4);
if (mode == UNM_NIU_PROMISCOUS_MODE) {
reg = (reg | 0x2000UL);
} else { /* FIXME use the correct mode value here */
reg = (reg & ~0x2000UL);
}
adapter->unm_crb_writelit_adapter(adapter,
UNM_NIU_XGE_CONFIG_1 + (0x10000 * port), reg);
}
/* turn the mac back on */
mac_cfg.rx_enable = 1;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_XGE_CONFIG_0 +
(0x10000 * port), &mac_cfg, 4);
}
return (result);
}
int
unm_niu_xg_set_tx_flow_ctl(struct unm_adapter_s *adapter, int enable)
{
int port = adapter->physical_port;
unm_niu_xg_pause_ctl_t reg;
if ((port < 0) || (port > UNM_NIU_MAX_XG_PORTS))
return (-1);
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_XG_PAUSE_CTL, &reg, 4);
if (port == 0)
reg.xg0_mask = !enable;
else
reg.xg1_mask = !enable;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_XG_PAUSE_CTL, &reg, 4);
return (0);
}
int
unm_niu_gbe_set_tx_flow_ctl(struct unm_adapter_s *adapter, int enable)
{
int port = adapter->physical_port;
unm_niu_gb_pause_ctl_t reg;
if ((port < 0) || (port > UNM_NIU_MAX_GBE_PORTS))
return (-1);
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_GB_PAUSE_CTL, &reg, 4);
switch (port) {
case (0):
reg.gb0_mask = !enable;
break;
case (1):
reg.gb1_mask = !enable;
break;
case (2):
reg.gb2_mask = !enable;
break;
case (3):
default:
reg.gb3_mask = !enable;
break;
}
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_PAUSE_CTL, &reg, 4);
return (0);
}
int
unm_niu_gbe_set_rx_flow_ctl(struct unm_adapter_s *adapter, int enable)
{
int port = adapter->physical_port;
unm_niu_gb_mac_config_0_t reg;
if ((port < 0) || (port > UNM_NIU_MAX_GBE_PORTS))
return (-1);
adapter->unm_nic_hw_read_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(port),
&reg, 4);
reg.rx_flowctl = enable;
adapter->unm_nic_hw_write_wx(adapter, UNM_NIU_GB_MAC_CONFIG_0(port),
&reg, 4);
return (0);
}