Skip to content

Commit

Permalink
sfc: Make PHY flash mode a device attribute, not a module parameter
Browse files Browse the repository at this point in the history
This allows updating PHY firmware for one interface without removing
all other interfaces handled by the driver.

Replace tx_disabled flags and 10Xpress status enumeration with flags in
enum efx_phy_mode.

Prevent an interface from being brought up while in PHY flash mode.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Ben Hutchings authored and Jeff Garzik committed Sep 3, 2008
1 parent 3594e13 commit f8b87c1
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 123 deletions.
2 changes: 0 additions & 2 deletions drivers/net/sfc/boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,5 @@ enum efx_board_type {

extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
extern int sfe4001_init(struct efx_nic *efx);
/* Are we putting the PHY into flash config mode */
extern unsigned int sfe4001_phy_flash_cfg;

#endif
3 changes: 3 additions & 0 deletions drivers/net/sfc/efx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,9 @@ static int efx_net_open(struct net_device *net_dev)
EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name,
raw_smp_processor_id());

if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY;

efx_start_all(efx);
return 0;
}
Expand Down
15 changes: 8 additions & 7 deletions drivers/net/sfc/falcon_xmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ static int falcon_reset_xmac(struct efx_nic *efx)
udelay(10);
}

/* This often fails when DSP is disabled, ignore it */
if (sfe4001_phy_flash_cfg)
return 0;

EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
return -ETIMEDOUT;
}
Expand Down Expand Up @@ -444,7 +440,8 @@ static bool falcon_check_xaui_link_up(struct efx_nic *efx)
max_tries = tries;

if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
(efx->phy_type == PHY_TYPE_NONE))
(efx->phy_type == PHY_TYPE_NONE) ||
efx_phy_mode_disabled(efx->phy_mode))
return false;

while (tries) {
Expand All @@ -471,7 +468,11 @@ void falcon_reconfigure_xmac(struct efx_nic *efx)

falcon_deconfigure_mac_wrapper(efx);

efx->tx_disabled = LOOPBACK_INTERNAL(efx);
/* Reconfigure the PHY, disabling transmit in mac level loopback. */
if (LOOPBACK_INTERNAL(efx))
efx->phy_mode |= PHY_MODE_TX_DISABLED;
else
efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
efx->phy_op->reconfigure(efx);

falcon_reconfigure_xgxs_core(efx);
Expand Down Expand Up @@ -566,7 +567,7 @@ int falcon_check_xmac(struct efx_nic *efx)
int rc;

if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
(efx->phy_type == PHY_TYPE_NONE))
efx_phy_mode_disabled(efx->phy_mode))
return 0;

falcon_mask_status_intr(efx, false);
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/sfc/mdio_10g.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
return true;
else if (efx->loopback_mode == LOOPBACK_NETWORK)
return false;
else if (efx_phy_mode_disabled(efx->phy_mode))
return false;
else if (efx->loopback_mode == LOOPBACK_PHYXS)
mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS |
MDIO_MMDREG_DEVS0_PCS |
Expand Down Expand Up @@ -206,7 +208,7 @@ void mdio_clause45_transmit_disable(struct efx_nic *efx)

ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
MDIO_MMDREG_TXDIS);
if (efx->tx_disabled)
if (efx->phy_mode & PHY_MODE_TX_DISABLED)
ctrl2 |= (1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
else
ctrl1 &= ~(1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
Expand Down
21 changes: 19 additions & 2 deletions drivers/net/sfc/net_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,23 @@ struct efx_phy_operations {
unsigned loopbacks;
};

/**
* @enum efx_phy_mode - PHY operating mode flags
* @PHY_MODE_NORMAL: on and should pass traffic
* @PHY_MODE_TX_DISABLED: on with TX disabled
* @PHY_MODE_SPECIAL: on but will not pass traffic
*/
enum efx_phy_mode {
PHY_MODE_NORMAL = 0,
PHY_MODE_TX_DISABLED = 1,
PHY_MODE_SPECIAL = 8,
};

static inline bool efx_phy_mode_disabled(enum efx_phy_mode mode)
{
return (mode & ~PHY_MODE_TX_DISABLED) != 0;
}

/*
* Efx extended statistics
*
Expand Down Expand Up @@ -661,7 +678,7 @@ union efx_multicast_hash {
* @phy_op: PHY interface
* @phy_data: PHY private data (including PHY-specific stats)
* @mii: PHY interface
* @tx_disabled: PHY transmitter turned off
* @phy_mode: PHY operating mode
* @link_up: Link status
* @link_options: Link options (MII/GMII format)
* @n_link_state_changes: Number of times the link has changed state
Expand Down Expand Up @@ -735,7 +752,7 @@ struct efx_nic {
struct efx_phy_operations *phy_op;
void *phy_data;
struct mii_if_info mii;
bool tx_disabled;
enum efx_phy_mode phy_mode;

bool link_up;
unsigned int link_options;
Expand Down
8 changes: 0 additions & 8 deletions drivers/net/sfc/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@
*/
extern struct efx_phy_operations falcon_tenxpress_phy_ops;

enum tenxpress_state {
TENXPRESS_STATUS_OFF = 0,
TENXPRESS_STATUS_OTEMP = 1,
TENXPRESS_STATUS_NORMAL = 2,
};

extern void tenxpress_set_state(struct efx_nic *efx,
enum tenxpress_state state);
extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
extern void tenxpress_crc_err(struct efx_nic *efx);

Expand Down
186 changes: 118 additions & 68 deletions drivers/net/sfc/sfe4001.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* the PHY
*/
#include <linux/delay.h>
#include "net_driver.h"
#include "efx.h"
#include "phy.h"
#include "boards.h"
Expand Down Expand Up @@ -120,52 +121,15 @@ static void sfe4001_poweroff(struct efx_nic *efx)
i2c_smbus_read_byte_data(hwmon_client, RSL);
}

static void sfe4001_fini(struct efx_nic *efx)
static int sfe4001_poweron(struct efx_nic *efx)
{
EFX_INFO(efx, "%s\n", __func__);

sfe4001_poweroff(efx);
i2c_unregister_device(efx->board_info.ioexp_client);
i2c_unregister_device(efx->board_info.hwmon_client);
}

/* The P0_EN_3V3X line on SFE4001 boards (from A2 onward) is connected
* to the FLASH_CFG_1 input on the DSP. We must keep it high at power-
* up to allow writing the flash (done through MDIO from userland).
*/
unsigned int sfe4001_phy_flash_cfg;
module_param_named(phy_flash_cfg, sfe4001_phy_flash_cfg, uint, 0444);
MODULE_PARM_DESC(phy_flash_cfg,
"Force PHY to enter flash configuration mode");

/* This board uses an I2C expander to provider power to the PHY, which needs to
* be turned on before the PHY can be used.
* Context: Process context, rtnl lock held
*/
int sfe4001_init(struct efx_nic *efx)
{
struct i2c_client *hwmon_client, *ioexp_client;
struct i2c_client *hwmon_client = efx->board_info.hwmon_client;
struct i2c_client *ioexp_client = efx->board_info.ioexp_client;
unsigned int i, j;
int rc;
u8 out;
efx_dword_t reg;

hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647);
if (!hwmon_client)
return -EIO;
efx->board_info.hwmon_client = hwmon_client;

ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
if (!ioexp_client) {
rc = -EIO;
goto fail_hwmon;
}
efx->board_info.ioexp_client = ioexp_client;

/* 10Xpress has fixed-function LED pins, so there is no board-specific
* blink code. */
efx->board_info.blink = tenxpress_phy_blink;

/* Ensure that XGXS and XAUI SerDes are held in reset */
EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1,
XX_PWRDNB_EN, 1,
Expand All @@ -177,33 +141,15 @@ int sfe4001_init(struct efx_nic *efx)
falcon_xmac_writel(efx, &reg, XX_PWR_RST_REG_MAC);
udelay(10);

efx->board_info.fini = sfe4001_fini;

/* Set DSP over-temperature alert threshold */
EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
rc = i2c_smbus_write_byte_data(hwmon_client, WLHO,
xgphy_max_temperature);
if (rc)
goto fail_ioexp;

/* Read it back and verify */
rc = i2c_smbus_read_byte_data(hwmon_client, RLHN);
if (rc < 0)
goto fail_ioexp;
if (rc != xgphy_max_temperature) {
rc = -EFAULT;
goto fail_ioexp;
}

/* Clear any previous over-temperature alert */
rc = i2c_smbus_read_byte_data(hwmon_client, RSL);
if (rc < 0)
goto fail_ioexp;
return rc;

/* Enable port 0 and port 1 outputs on IO expander */
rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00);
if (rc)
goto fail_ioexp;
return rc;
rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG,
0xff & ~(1 << P1_SPARE_LBN));
if (rc)
Expand Down Expand Up @@ -231,7 +177,7 @@ int sfe4001_init(struct efx_nic *efx)
out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
(1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
(1 << P0_X_TRST_LBN));
if (sfe4001_phy_flash_cfg)
if (efx->phy_mode & PHY_MODE_SPECIAL)
out |= 1 << P0_EN_3V3X_LBN;

rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
Expand All @@ -250,9 +196,9 @@ int sfe4001_init(struct efx_nic *efx)
/* In flash config mode, DSP does not turn on AFE, so
* just wait 1 second.
*/
if (sfe4001_phy_flash_cfg) {
if (efx->phy_mode & PHY_MODE_SPECIAL) {
schedule_timeout_uninterruptible(HZ);
goto done;
return 0;
}

for (j = 0; j < 10; ++j) {
Expand All @@ -263,23 +209,127 @@ int sfe4001_init(struct efx_nic *efx)
if (rc < 0)
goto fail_on;
if (rc & (1 << P1_AFE_PWD_LBN))
goto done;
return 0;
}
}

EFX_INFO(efx, "timed out waiting for DSP boot\n");
rc = -ETIMEDOUT;
goto fail_on;
fail_on:
sfe4001_poweroff(efx);
return rc;
}

/* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin
* using the 3V3X output of the IO-expander. Allow the user to set
* this when the device is stopped, and keep it stopped then.
*/

static ssize_t show_phy_flash_cfg(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL));
}

static ssize_t set_phy_flash_cfg(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
enum efx_phy_mode old_mode, new_mode;
int err;

rtnl_lock();
old_mode = efx->phy_mode;
if (count == 0 || *buf == '0')
new_mode = old_mode & ~PHY_MODE_SPECIAL;
else
new_mode = PHY_MODE_SPECIAL;
if (old_mode == new_mode) {
err = 0;
} else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) {
err = -EBUSY;
} else {
efx->phy_mode = new_mode;
err = sfe4001_poweron(efx);
efx_reconfigure_port(efx);
}
rtnl_unlock();

return err ? err : count;
}

static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg);

static void sfe4001_fini(struct efx_nic *efx)
{
EFX_INFO(efx, "%s\n", __func__);

device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
sfe4001_poweroff(efx);
i2c_unregister_device(efx->board_info.ioexp_client);
i2c_unregister_device(efx->board_info.hwmon_client);
}

/* This board uses an I2C expander to provider power to the PHY, which needs to
* be turned on before the PHY can be used.
* Context: Process context, rtnl lock held
*/
int sfe4001_init(struct efx_nic *efx)
{
struct i2c_client *hwmon_client;
int rc;

hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647);
if (!hwmon_client)
return -EIO;
efx->board_info.hwmon_client = hwmon_client;

/* Set DSP over-temperature alert threshold */
EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
rc = i2c_smbus_write_byte_data(hwmon_client, WLHO,
xgphy_max_temperature);
if (rc)
goto fail_ioexp;

/* Read it back and verify */
rc = i2c_smbus_read_byte_data(hwmon_client, RLHN);
if (rc < 0)
goto fail_ioexp;
if (rc != xgphy_max_temperature) {
rc = -EFAULT;
goto fail_ioexp;
}

efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
if (!efx->board_info.ioexp_client) {
rc = -EIO;
goto fail_hwmon;
}

/* 10Xpress has fixed-function LED pins, so there is no board-specific
* blink code. */
efx->board_info.blink = tenxpress_phy_blink;

efx->board_info.fini = sfe4001_fini;

rc = sfe4001_poweron(efx);
if (rc)
goto fail_ioexp;

rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
if (rc)
goto fail_on;

done:
EFX_INFO(efx, "PHY is powered on\n");
return 0;

fail_on:
sfe4001_poweroff(efx);
fail_ioexp:
i2c_unregister_device(ioexp_client);
i2c_unregister_device(efx->board_info.ioexp_client);
fail_hwmon:
i2c_unregister_device(hwmon_client);
i2c_unregister_device(hwmon_client);
return rc;
}
Loading

0 comments on commit f8b87c1

Please sign in to comment.