sfc: Add support for QT2025C PHY

This is a new PHY supporting SFP+ modules, used in the SFN4112F
reference design.  It is similar to the QT2022C2 and shares much of
its support code.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ben Hutchings 2009-02-27 13:07:33 +00:00 committed by David S. Miller
parent 3f39a5e9bf
commit d2d2c37314
4 changed files with 91 additions and 10 deletions

View File

@ -2249,6 +2249,7 @@ static int falcon_probe_phy(struct efx_nic *efx)
efx->phy_op = &falcon_sft9001_phy_ops;
break;
case PHY_TYPE_QT2022C2:
case PHY_TYPE_QT2025C:
efx->phy_op = &falcon_xfp_phy_ops;
break;
default:

View File

@ -450,6 +450,7 @@ enum phy_type {
PHY_TYPE_QT2022C2 = 4,
PHY_TYPE_PM8358 = 6,
PHY_TYPE_SFT9001A = 8,
PHY_TYPE_QT2025C = 9,
PHY_TYPE_SFT9001B = 10,
PHY_TYPE_MAX /* Insert any new items before this */
};

View File

@ -23,11 +23,11 @@ extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
extern int sft9001_wait_boot(struct efx_nic *efx);
/****************************************************************************
* Exported functions from the driver for XFP optical PHYs
* AMCC/Quake QT20xx PHYs
*/
extern struct efx_phy_operations falcon_xfp_phy_ops;
/* The QUAKE XFP PHY provides various H/W control states for LEDs */
/* These PHYs provide various H/W control states for LEDs */
#define QUAKE_LED_LINK_INVAL (0)
#define QUAKE_LED_LINK_STAT (1)
#define QUAKE_LED_LINK_ACT (2)

View File

@ -7,8 +7,8 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
/*
* Driver for XFP optical PHYs (plus some support specific to the Quake 2022/32)
* See www.amcc.com for details (search for qt2032)
* Driver for SFP+ and XFP optical PHYs plus some support specific to the
* AMCC QT20xx adapters; see www.amcc.com for details
*/
#include <linux/timer.h>
@ -31,6 +31,21 @@
/* Quake-specific MDIO registers */
#define MDIO_QUAKE_LED0_REG (0xD006)
/* QT2025C only */
#define PCS_FW_HEARTBEAT_REG 0xd7ee
#define PCS_FW_HEARTB_LBN 0
#define PCS_FW_HEARTB_WIDTH 8
#define PCS_UC8051_STATUS_REG 0xd7fd
#define PCS_UC_STATUS_LBN 0
#define PCS_UC_STATUS_WIDTH 8
#define PCS_UC_STATUS_FW_SAVE 0x20
#define PMA_PMD_FTX_CTRL2_REG 0xc309
#define PMA_PMD_FTX_STATIC_LBN 13
#define PMA_PMD_VEND1_REG 0xc001
#define PMA_PMD_VEND1_LBTXD_LBN 15
#define PCS_VEND1_REG 0xc000
#define PCS_VEND1_LBTXD_LBN 5
void xfp_set_led(struct efx_nic *p, int led, int mode)
{
int addr = MDIO_QUAKE_LED0_REG + led;
@ -45,7 +60,49 @@ struct xfp_phy_data {
#define XFP_MAX_RESET_TIME 500
#define XFP_RESET_WAIT 10
/* Reset the PHYXS MMD. This is documented (for the Quake PHY) as doing
static int qt2025c_wait_reset(struct efx_nic *efx)
{
unsigned long timeout = jiffies + 10 * HZ;
int phy_id = efx->mii.phy_id;
int reg, old_counter = 0;
/* Wait for firmware heartbeat to start */
for (;;) {
int counter;
reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
PCS_FW_HEARTBEAT_REG);
if (reg < 0)
return reg;
counter = ((reg >> PCS_FW_HEARTB_LBN) &
((1 << PCS_FW_HEARTB_WIDTH) - 1));
if (old_counter == 0)
old_counter = counter;
else if (counter != old_counter)
break;
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(10);
}
/* Wait for firmware status to look good */
for (;;) {
reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
PCS_UC8051_STATUS_REG);
if (reg < 0)
return reg;
if ((reg &
((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >=
PCS_UC_STATUS_FW_SAVE)
break;
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(100);
}
return 0;
}
/* Reset the PHYXS MMD. This is documented (for the Quake PHYs) as doing
* a complete soft reset.
*/
static int xfp_reset_phy(struct efx_nic *efx)
@ -58,6 +115,12 @@ static int xfp_reset_phy(struct efx_nic *efx)
if (rc < 0)
goto fail;
if (efx->phy_type == PHY_TYPE_QT2025C) {
rc = qt2025c_wait_reset(efx);
if (rc < 0)
goto fail;
}
/* Wait 250ms for the PHY to complete bootup */
msleep(250);
@ -131,12 +194,28 @@ static void xfp_phy_reconfigure(struct efx_nic *efx)
{
struct xfp_phy_data *phy_data = efx->phy_data;
/* Reset the PHY when moving from tx off to tx on */
if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) &&
(phy_data->phy_mode & PHY_MODE_TX_DISABLED))
xfp_reset_phy(efx);
if (efx->phy_type == PHY_TYPE_QT2025C) {
/* There are several different register bits which can
* disable TX (and save power) on direct-attach cables
* or optical transceivers, varying somewhat between
* firmware versions. Only 'static mode' appears to
* cover everything. */
mdio_clause45_set_flag(
efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
PMA_PMD_FTX_CTRL2_REG, PMA_PMD_FTX_STATIC_LBN,
efx->phy_mode & PHY_MODE_TX_DISABLED ||
efx->phy_mode & PHY_MODE_LOW_POWER ||
efx->loopback_mode == LOOPBACK_PCS ||
efx->loopback_mode == LOOPBACK_PMAPMD);
} else {
/* Reset the PHY when moving from tx off to tx on */
if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) &&
(phy_data->phy_mode & PHY_MODE_TX_DISABLED))
xfp_reset_phy(efx);
mdio_clause45_transmit_disable(efx);
}
mdio_clause45_transmit_disable(efx);
mdio_clause45_phy_reconfigure(efx);
phy_data->phy_mode = efx->phy_mode;