/*
* 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) 2003-2005 Chelsio Communications. All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI" /* ch_subr.c */
#include "common.h"
#include "elmer0.h"
#include "regs.h"
#include "gmac.h"
#include "cphy.h"
#include "sge.h"
#include "tp.h"
#include "espi.h"
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#include "mc3.h"
#include "mc4.h"
#include "mc5.h"
#include "ulp.h"
#endif
#ifdef CONFIG_CHELSIO_T1_COUGAR
#include "cspi.h"
#endif
/*
* t1_wait_op_done - wait until an operation is completed
* @adapter: the adapter performing the operation
* @reg: the register to check for completion
* @mask: a single-bit field within @reg that indicates completion
* @polarity: the value of the field when the operation is completed
* @attempts: number of check iterations
* @delay: delay in usecs between iterations
* @attempts: number of check iterations
* @delay: delay in usecs between iterations
*
* Wait until an operation is completed by checking a bit in a register
* up to @attempts times. Returns %0 if the operation completes and %1
* otherwise.
*/
{
while (attempts) {
return (0);
if (--attempts == 0)
return (1);
if (delay)
}
return (1);
}
/* #define TPI_ATTEMPTS 50 */
/*
* Write a register over the TPI interface (unlocked and locked versions).
*/
int
{
int tpi_busy;
TPI_ATTEMPTS, 3);
if (tpi_busy)
CH_ALERT("%s: TPI write to 0x%x failed\n",
return (tpi_busy);
}
int
{
int ret;
return (ret);
}
/*
* Read a register over the TPI interface (unlocked and locked versions).
*/
int
{
int tpi_busy;
TPI_ATTEMPTS, 3);
if (tpi_busy)
CH_ALERT("%s: TPI read from 0x%x failed\n",
else
return (tpi_busy);
}
int
{
int ret;
return (ret);
}
/*
* Set a TPI parameter.
*/
{
}
/*
* Called when a port's link settings change to propagate the new values to the
* associated PHY and MAC. After performing the common tasks it invokes an
* OS-specific handler.
*/
void
{
/* Set MAC speed, duplex, and flow control to match PHY. */
}
}
{
&pcix_cause);
if (pcix_cause) {
}
return (0);
}
#ifdef CONFIG_CHELSIO_T1_1G
#include "fpga_defs.h"
/*
* PHY interrupt handler for FPGA boards.
*/
{
int p;
if (cause & (1 << p)) {
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, p);
}
return (0);
}
/*
* Slow path interrupt handler for FPGAs.
*/
{
cause &= ~F_PL_INTR_SGE_DATA;
if (cause & F_PL_INTR_SGE_ERR)
if (cause & FPGA_PCIX_INTERRUPT_GMAC)
(void) fpga_phy_intr_handler(adapter);
if (cause & FPGA_PCIX_INTERRUPT_TP) {
/*
* FPGA doesn't support MC4 interrupts and it requires
* this odd layer of indirection for MC5.
*/
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
if (tp_cause & FPGA_TP_INTERRUPT_MC5)
#endif
/* Clear TP interrupt */
tp_cause);
}
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
if (cause & FPGA_PCIX_INTERRUPT_MC3)
#endif
if (cause & FPGA_PCIX_INTERRUPT_PCIX)
(void) t1_pci_intr_handler(adapter);
/* Clear the interrupts just processed. */
if (cause)
return (cause != 0);
}
/*
* FPGA MDIO initialization.
*/
{
(void) bi; /* avoid warnings */
}
/*
*/
{
if (mmd_addr)
return (-EINVAL);
/* Check if MDI is busy; this shouldn't happen. */
CH_ALERT("%s: MDIO busy at start of read\n",
return (-EBUSY);
}
return (0);
}
{
if (mmd_addr)
return (-EINVAL);
/* Check if MDI is busy; this shouldn't happen. */
CH_ALERT("%s: MDIO busy at start of write\n",
return (-EBUSY);
}
return (0);
}
};
#endif
/*
* Wait until Elmer's MI1 interface is ready for new operations.
*/
{
do {
if (busy)
DELAY_US(10);
if (busy)
CH_ALERT("%s: MDIO operation timed out\n",
return (busy);
}
/*
* MI1 MDIO initialization.
*/
{
}
#if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR)
/*
*/
{
if (mmd_addr)
return (-EINVAL);
return (0);
}
{
if (mmd_addr)
return (-EINVAL);
return (0);
}
#if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR)
};
#endif
#endif
#if 0
{
/* Write the address we want. */
/* Write the operation we want. */
/* Read the data. */
return (0);
}
#endif
{
/* Write the address we want. */
/* Write the operation we want. */
/* Read the data. */
return (0);
}
{
/* Write the address we want. */
/* Write the data. */
return (0);
}
};
enum {
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#ifdef CONFIG_CHELSIO_T1_1G
#endif
#ifdef CONFIG_CHELSIO_T1_COUGAR
#ifdef CONFIG_CHELSIO_T1_1G
#endif
#endif
#ifdef CONFIG_USERMODE
#endif
#endif
};
125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/,
"Chelsio T110 1x10GBase-CX4 TOE" },
125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
"Chelsio N110 1x10GBaseX NIC" },
125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
"Chelsio N210 1x10GBaseX NIC" },
125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
"Chelsio T210 1x10GBaseX TOE" },
125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/,
"Chelsio T210 1x10GBase-CX4 TOE" },
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#ifdef CONFIG_CHELSIO_T1_1G
100000000/*clk-core*/, 133000000/*clk-mc3*/, 100000000/*clk-mc4*/,
4/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
"Chelsio T204 4x100/1000BaseT TOE" },
100000000/*clk-core*/, 133000000/*clk-mc3*/, 100000000/*clk-mc4*/,
4/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
"Chelsio T204V 4x100/1000BaseT TOE" },
16000000/*clk-core*/, 16000000/*clk-mc3*/, 16000000/*clk-mc4*/,
0/*espi-ports*/, 0/*clk-cspi*/, 0/*clk-elmer0*/, 0/*mdien*/,
"Chelsio FPGA 4x10/100/1000BaseT TOE" },
87500000/*clk-core*/, 87500000/*clk-mc3*/, 87500000/*clk-mc4*/,
4/*espi-ports*/, 0/*clk-cspi*/, 40/*clk-elmer0*/, 0/*mdien*/,
"Chelsio 7500 4x100/1000BaseT TOE" },
87500000/*clk-core*/, 87500000/*clk-mc3*/, 87500000/*clk-mc4*/,
4/*espi-ports*/, 0/*clk-cspi*/, 40/*clk-elmer0*/, 0/*mdien*/,
"Chelsio 7500 4x1000BaseX TOE" },
SUPPORTED_TP | SUPPORTED_PAUSE | SUPPORTED_LOOPBACK /*caps*/, CHBT_TERM_T1, CHBT_MAC_IXF1010, CHBT_PHY_88E1111,
83300000/*clk-core*/, 83300000/*clk-mc3*/, 83300000/*clk-mc4*/,
2/*espi-ports*/, 0/*clk-cspi*/, 40/*clk-elmer0*/, 0/*mdien*/,
"Chelsio T101 1x100/1000BaseT TOE" },
SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE | SUPPORTED_PAUSE | SUPPORTED_LOOPBACK /*caps*/,
83300000/*clk-core*/, 83300000/*clk-mc3*/, 83300000/*clk-mc4*/,
2/*espi-ports*/, 0/*clk-cspi*/, 40/*clk-elmer0*/, 0/*mdien*/,
"Chelsio T101 1x1000BaseX TOE" },
#endif
125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 40/*clk-elmer0*/, 1/*mdien*/,
"Chelsio 8000 1x10GBaseX TOE" },
125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/,
"Chelsio T110 1x10GBaseX TOE" },
#ifdef CONFIG_CHELSIO_T1_COUGAR
#ifdef CONFIG_CHELSIO_T1_1G
87500000/*clk-core*/, 87500000/*clk-mc3*/, 87500000/*clk-mc4*/,
4/*espi-ports*/, 333300000/*clk-cspi*/, 40/*clk-elmer0*/, 0/*mdien*/,
"Chelsio Cougar 4x100/1000BaseT TOE" },
#endif
87500000/*clk-core*/, 87500000/*clk-mc3*/, 87500000/*clk-mc4*/,
1/*espi-ports*/, 333300000/*clk-cspi*/, 40/*clk-elmer0*/, 1/*mdien*/,
"Chelsio Cougar 1x10GBaseX TOE" },
#endif
#ifdef CONFIG_USERMODE
125000000/*clk-core*/, 125000000/*clk-mc3*/, 125000000/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 0/*clk-elmer0*/, 0/*mdien*/,
#endif
#endif
};
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#ifdef CONFIG_CHELSIO_T1_1G
#endif
#ifdef CONFIG_CHELSIO_T1_COUGAR
#ifdef CONFIG_CHELSIO_T1_1G
#endif
#endif
#ifdef CONFIG_USERMODE
#endif
#endif
{ 0, }
};
#ifndef CH_DEVICE_COMMON
/*
* Return the board_info structure with a given index. Out-of-range indices
* return NULL.
*/
const struct board_info *
{
}
#else
/*
* pair. Return NULL if the id combination is unknown.
*/
unsigned short ssid)
{
struct pci_device_id *p;
for (p = t1_pci_tbl; p->devid; ++p)
return (&t1_board[p->board_info_index]);
return (NULL);
}
#endif
typedef struct {
/*
* Read SEEPROM. A zero is written to the flag register when the addres is
* written to the Control register. The hardware device will set the flag to a
* one when 4B have been transferred to the Data register.
*/
int
{
int i = EEPROM_MAX_POLL;
return (-EINVAL);
do {
DELAY_US(50);
(void) t1_os_pci_read_config_2(adapter,
A_PCICFG_VPD_ADDR, &val);
} while (!(val & F_VPD_OP_FLAG) && --i);
if (!(val & F_VPD_OP_FLAG)) {
CH_ERR("%s: reading EEPROM address 0x%x failed\n",
return (-EIO);
}
return (0);
}
{
return (ret);
}
/*
* Read a port's MAC address from the VPD ROM.
*/
{
return (1);
return (0);
}
/*
*
* If the PHY can auto-negotiate first decide what to advertise, then
*
* If the PHY does not auto-negotiate we just reset it.
*
* otherwise do it later based on the outcome of auto-negotiation.
*/
int
{
if (fc) {
else {
}
}
/* Also disables autoneg */
} else {
}
} else {
}
return 0;
}
/*
* External interrupt handler for boards using elmer0.
*/
int
{
int phy_cause;
#ifdef CONFIG_CHELSIO_T1_1G
case CHBT_BOARD_CHT204:
case CHBT_BOARD_CHT204V: {
int i, port_bit;
port_bit = i ? i + 1 : 0;
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, i);
}
break;
}
case CHBT_BOARD_CHT101:
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, 0);
}
break;
case CHBT_BOARD_7500: {
int p;
/*
* Elmer0's interrupt cause isn't useful here because there is
* only one bit that can be set for all 4 ports. This means
* we are forced to check every PHY's interrupt status
* register to see who initiated the interrupt.
*/
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, p);
}
break;
}
#endif
case CHBT_BOARD_CHT210:
case CHBT_BOARD_N210:
case CHBT_BOARD_N110:
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, 0);
}
break;
case CHBT_BOARD_8000:
case CHBT_BOARD_CHT110:
cause);
}
&mod_detect);
}
break;
#ifdef CONFIG_CHELSIO_T1_COUGAR
case CHBT_BOARD_COUGAR:
}
}
} else {
int i, port_bit;
port_bit = i ? i + 1 : 0;
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, i);
}
}
break;
#endif
}
return (0);
}
/* Enables all interrupts. */
void
{
unsigned int i;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
/*
* T2 -- Disable interrupts for now b/c we are not clearing
* correctly yet.
*/
/* t1_ulp_intr_enable(adapter->ulp); */
}
#endif
}
}
/* Enable PCIX & external chip interrupts on ASIC boards. */
if (t1_is_asic(adapter)) {
/* PCI-X interrupts */
0xffffffff);
}
}
/* Disables all interrupts. */
void
{
unsigned int i;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
}
#endif
}
/* Disable PCIX & external chip interrupts. */
if (t1_is_asic(adapter))
/* PCI-X interrupts */
adapter->slow_intr_mask = 0;
}
/* Clears all interrupts */
void
{
unsigned int i;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
}
#endif
}
/* Enable interrupts for external devices. */
if (t1_is_asic(adapter)) {
}
/* PCI-X interrupts */
0xffffffff);
}
/*
* Slow path interrupt handler for ASICs.
*/
{
if (!cause)
return (0);
if (cause & F_PL_INTR_SGE_ERR)
if (cause & F_PL_INTR_TP)
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
if (cause & F_PL_INTR_MC3)
if (cause & F_PL_INTR_MC4)
if (cause & F_PL_INTR_ULP)
if (cause & F_PL_INTR_MC5)
#endif
if (cause & F_PL_INTR_ESPI)
if (cause & F_PL_INTR_PCIX)
(void) t1_pci_intr_handler(adapter);
if (cause & F_PL_INTR_EXT)
/* Clear the interrupts just processed. */
return (1);
}
int
{
#ifdef CONFIG_CHELSIO_T1_1G
if (!t1_is_asic(adapter))
return (fpga_slow_intr(adapter));
#endif
return (asic_slow_intr(adapter));
}
/* Power sequencing is a work-around for Intel's XPAKs. */
static void
{
/* Check for XPAK */
if (!(ELMER0_GP_BIT5 & mod_detect)) {
/* XPAK is present */
gpo |= ELMER0_GP_BIT18;
}
}
struct adapter_params *p)
{
if (p->chip_version == CHBT_TERM_T1 ||
p->chip_version == CHBT_TERM_T2 ||
p->chip_version == CHBT_TERM_FPGA) {
if (val == 2)
p->chip_revision = TERM_T1B;
else if (val == 3)
p->chip_revision = TERM_T2;
else
return (-1);
} else
return (-1);
return (0);
}
/*
* Enable board components other than the Chelsio chip, such as external MAC
* and PHY.
*/
{
case CHBT_BOARD_8000:
case CHBT_BOARD_N110:
case CHBT_BOARD_N210:
case CHBT_BOARD_CHT210:
case CHBT_BOARD_COUGAR:
break;
case CHBT_BOARD_CHT110:
/*
* TBD XXX Might not need. This fixes a problem
* described in the Intel SR XPAK errata.
*/
break;
#ifdef CONFIG_CHELSIO_T1_1G
case CHBT_BOARD_CHT204:
case CHBT_BOARD_CHT204V:
break;
case CHBT_BOARD_CHT101:
case CHBT_BOARD_7500:
break;
#endif
}
return (0);
}
/*
* Initialize and configure the Terminator HW modules. Note that external
* MAC and PHYs are initialized separately.
*/
int
{
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
goto out_err;
goto out_err;
goto out_err;
goto out_err;
#endif
}
#ifdef CONFIG_CHELSIO_T1_COUGAR
goto out_err;
#endif
bi->espi_nports))
goto out_err;
goto out_err;
if (err)
goto out_err;
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#endif
err = 0;
return (err);
}
/*
* Determine a card's PCI mode.
*/
{
}
/*
* Release the structures holding the SW per-Terminator-HW-module state.
*/
void
{
unsigned int i;
if (mac)
if (phy)
}
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
#endif
#ifdef CONFIG_CHELSIO_T1_COUGAR
#endif
}
const struct board_info *bi)
{
} else {
lc->advertising = 0;
}
}
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
{
mtus[0] = 68;
}
#endif
/*
* Allocate and initialize the data structures that hold the SW state of
* the Terminator HW modules.
*/
const struct board_info *bi)
{
unsigned int i;
CH_ERR("%s: SGE initialization failed\n",
goto error;
}
#ifdef CONFIG_CHELSIO_T1_OFFLOAD
/*
* Must wait 200us after power up before touching the
* memory controllers.
*/
DELAY_US(200);
CH_ERR("%s: MC3 initialization failed\n",
goto error;
}
CH_ERR("%s: MC4 initialization failed\n",
goto error;
}
CH_ERR("%s: MC5 initialization failed\n",
goto error;
}
CH_ERR("%s: ULP initialization failed\n",
goto error;
}
}
#endif
#ifdef CONFIG_CHELSIO_T1_COUGAR
CH_ERR("%s: CSPI initialization failed\n",
goto error;
}
#endif
CH_ERR("%s: ESPI initialization failed\n",
goto error;
}
CH_ERR("%s: TP initialization failed\n",
goto error;
}
CH_ERR("%s: PHY %d initialization failed\n",
adapter_name(adapter), i);
goto error;
}
if (!mac) {
CH_ERR("%s: MAC %d initialization failed\n",
adapter_name(adapter), i);
goto error;
}
/*
* Get the port's MAC addresses either from the EEPROM if one
* exists or the one hardcoded in the MAC.
*/
CH_ERR("%s: could not read MAC address from VPD ROM\n",
goto error;
}
}
return (0);
return (-1);
}