Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.32 250 lines 6.5 kB view raw
1/**************************************************************************** 2 * Driver for Solarflare Solarstorm network controllers and boards 3 * Copyright 2006-2008 Solarflare Communications Inc. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 as published 7 * by the Free Software Foundation, incorporated herein by reference. 8 */ 9/* 10 * Driver for SFP+ and XFP optical PHYs plus some support specific to the 11 * AMCC QT20xx adapters; see www.amcc.com for details 12 */ 13 14#include <linux/timer.h> 15#include <linux/delay.h> 16#include "efx.h" 17#include "mdio_10g.h" 18#include "phy.h" 19#include "falcon.h" 20 21#define XFP_REQUIRED_DEVS (MDIO_DEVS_PCS | \ 22 MDIO_DEVS_PMAPMD | \ 23 MDIO_DEVS_PHYXS) 24 25#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \ 26 (1 << LOOPBACK_PMAPMD) | \ 27 (1 << LOOPBACK_NETWORK)) 28 29/****************************************************************************/ 30/* Quake-specific MDIO registers */ 31#define MDIO_QUAKE_LED0_REG (0xD006) 32 33/* QT2025C only */ 34#define PCS_FW_HEARTBEAT_REG 0xd7ee 35#define PCS_FW_HEARTB_LBN 0 36#define PCS_FW_HEARTB_WIDTH 8 37#define PCS_UC8051_STATUS_REG 0xd7fd 38#define PCS_UC_STATUS_LBN 0 39#define PCS_UC_STATUS_WIDTH 8 40#define PCS_UC_STATUS_FW_SAVE 0x20 41#define PMA_PMD_FTX_CTRL2_REG 0xc309 42#define PMA_PMD_FTX_STATIC_LBN 13 43#define PMA_PMD_VEND1_REG 0xc001 44#define PMA_PMD_VEND1_LBTXD_LBN 15 45#define PCS_VEND1_REG 0xc000 46#define PCS_VEND1_LBTXD_LBN 5 47 48void xfp_set_led(struct efx_nic *p, int led, int mode) 49{ 50 int addr = MDIO_QUAKE_LED0_REG + led; 51 efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode); 52} 53 54struct xfp_phy_data { 55 enum efx_phy_mode phy_mode; 56}; 57 58#define XFP_MAX_RESET_TIME 500 59#define XFP_RESET_WAIT 10 60 61static int qt2025c_wait_reset(struct efx_nic *efx) 62{ 63 unsigned long timeout = jiffies + 10 * HZ; 64 int reg, old_counter = 0; 65 66 /* Wait for firmware heartbeat to start */ 67 for (;;) { 68 int counter; 69 reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_FW_HEARTBEAT_REG); 70 if (reg < 0) 71 return reg; 72 counter = ((reg >> PCS_FW_HEARTB_LBN) & 73 ((1 << PCS_FW_HEARTB_WIDTH) - 1)); 74 if (old_counter == 0) 75 old_counter = counter; 76 else if (counter != old_counter) 77 break; 78 if (time_after(jiffies, timeout)) 79 return -ETIMEDOUT; 80 msleep(10); 81 } 82 83 /* Wait for firmware status to look good */ 84 for (;;) { 85 reg = efx_mdio_read(efx, MDIO_MMD_PCS, PCS_UC8051_STATUS_REG); 86 if (reg < 0) 87 return reg; 88 if ((reg & 89 ((1 << PCS_UC_STATUS_WIDTH) - 1) << PCS_UC_STATUS_LBN) >= 90 PCS_UC_STATUS_FW_SAVE) 91 break; 92 if (time_after(jiffies, timeout)) 93 return -ETIMEDOUT; 94 msleep(100); 95 } 96 97 return 0; 98} 99 100static int xfp_reset_phy(struct efx_nic *efx) 101{ 102 int rc; 103 104 if (efx->phy_type == PHY_TYPE_QT2025C) { 105 /* Wait for the reset triggered by falcon_reset_hw() 106 * to complete */ 107 rc = qt2025c_wait_reset(efx); 108 if (rc < 0) 109 goto fail; 110 } else { 111 /* Reset the PHYXS MMD. This is documented as doing 112 * a complete soft reset. */ 113 rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PHYXS, 114 XFP_MAX_RESET_TIME / XFP_RESET_WAIT, 115 XFP_RESET_WAIT); 116 if (rc < 0) 117 goto fail; 118 } 119 120 /* Wait 250ms for the PHY to complete bootup */ 121 msleep(250); 122 123 /* Check that all the MMDs we expect are present and responding. We 124 * expect faults on some if the link is down, but not on the PHY XS */ 125 rc = efx_mdio_check_mmds(efx, XFP_REQUIRED_DEVS, MDIO_DEVS_PHYXS); 126 if (rc < 0) 127 goto fail; 128 129 efx->board_info.init_leds(efx); 130 131 return rc; 132 133 fail: 134 EFX_ERR(efx, "PHY reset timed out\n"); 135 return rc; 136} 137 138static int xfp_phy_init(struct efx_nic *efx) 139{ 140 struct xfp_phy_data *phy_data; 141 u32 devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS); 142 int rc; 143 144 phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL); 145 if (!phy_data) 146 return -ENOMEM; 147 efx->phy_data = phy_data; 148 149 EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n", 150 devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), 151 efx_mdio_id_rev(devid)); 152 153 phy_data->phy_mode = efx->phy_mode; 154 155 rc = xfp_reset_phy(efx); 156 157 EFX_INFO(efx, "PHY init %s.\n", 158 rc ? "failed" : "successful"); 159 if (rc < 0) 160 goto fail; 161 162 return 0; 163 164 fail: 165 kfree(efx->phy_data); 166 efx->phy_data = NULL; 167 return rc; 168} 169 170static void xfp_phy_clear_interrupt(struct efx_nic *efx) 171{ 172 /* Read to clear link status alarm */ 173 efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT); 174} 175 176static int xfp_link_ok(struct efx_nic *efx) 177{ 178 return efx_mdio_links_ok(efx, XFP_REQUIRED_DEVS); 179} 180 181static void xfp_phy_poll(struct efx_nic *efx) 182{ 183 int link_up = xfp_link_ok(efx); 184 /* Simulate a PHY event if link state has changed */ 185 if (link_up != efx->link_up) 186 falcon_sim_phy_event(efx); 187} 188 189static void xfp_phy_reconfigure(struct efx_nic *efx) 190{ 191 struct xfp_phy_data *phy_data = efx->phy_data; 192 193 if (efx->phy_type == PHY_TYPE_QT2025C) { 194 /* There are several different register bits which can 195 * disable TX (and save power) on direct-attach cables 196 * or optical transceivers, varying somewhat between 197 * firmware versions. Only 'static mode' appears to 198 * cover everything. */ 199 mdio_set_flag( 200 &efx->mdio, efx->mdio.prtad, MDIO_MMD_PMAPMD, 201 PMA_PMD_FTX_CTRL2_REG, 1 << PMA_PMD_FTX_STATIC_LBN, 202 efx->phy_mode & PHY_MODE_TX_DISABLED || 203 efx->phy_mode & PHY_MODE_LOW_POWER || 204 efx->loopback_mode == LOOPBACK_PCS || 205 efx->loopback_mode == LOOPBACK_PMAPMD); 206 } else { 207 /* Reset the PHY when moving from tx off to tx on */ 208 if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) && 209 (phy_data->phy_mode & PHY_MODE_TX_DISABLED)) 210 xfp_reset_phy(efx); 211 212 efx_mdio_transmit_disable(efx); 213 } 214 215 efx_mdio_phy_reconfigure(efx); 216 217 phy_data->phy_mode = efx->phy_mode; 218 efx->link_up = xfp_link_ok(efx); 219 efx->link_speed = 10000; 220 efx->link_fd = true; 221 efx->link_fc = efx->wanted_fc; 222} 223 224static void xfp_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) 225{ 226 mdio45_ethtool_gset(&efx->mdio, ecmd); 227} 228 229static void xfp_phy_fini(struct efx_nic *efx) 230{ 231 /* Clobber the LED if it was blinking */ 232 efx->board_info.blink(efx, false); 233 234 /* Free the context block */ 235 kfree(efx->phy_data); 236 efx->phy_data = NULL; 237} 238 239struct efx_phy_operations falcon_xfp_phy_ops = { 240 .macs = EFX_XMAC, 241 .init = xfp_phy_init, 242 .reconfigure = xfp_phy_reconfigure, 243 .poll = xfp_phy_poll, 244 .fini = xfp_phy_fini, 245 .clear_interrupt = xfp_phy_clear_interrupt, 246 .get_settings = xfp_phy_get_settings, 247 .set_settings = efx_mdio_set_settings, 248 .mmds = XFP_REQUIRED_DEVS, 249 .loopbacks = XFP_LOOPBACKS, 250};