diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2008-09-01 07:48:17 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-09-03 09:53:47 -0400 |
commit | f8b87c17017f2ce1890fb9a7f85fb0fbf5643e37 (patch) | |
tree | 261a31a95d2fa296ef7abaf6019ff5b7c86bbc63 /drivers/net/sfc/sfe4001.c | |
parent | 3594e131b23665b728b4c98daaf0b61b1d4aaa7a (diff) |
sfc: Make PHY flash mode a device attribute, not a module parameter
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>
Diffstat (limited to 'drivers/net/sfc/sfe4001.c')
-rw-r--r-- | drivers/net/sfc/sfe4001.c | 186 |
1 files changed, 118 insertions, 68 deletions
diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index f9272816437d..d90fab59f83e 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c | |||
@@ -13,6 +13,7 @@ | |||
13 | * the PHY | 13 | * the PHY |
14 | */ | 14 | */ |
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include "net_driver.h" | ||
16 | #include "efx.h" | 17 | #include "efx.h" |
17 | #include "phy.h" | 18 | #include "phy.h" |
18 | #include "boards.h" | 19 | #include "boards.h" |
@@ -120,52 +121,15 @@ static void sfe4001_poweroff(struct efx_nic *efx) | |||
120 | i2c_smbus_read_byte_data(hwmon_client, RSL); | 121 | i2c_smbus_read_byte_data(hwmon_client, RSL); |
121 | } | 122 | } |
122 | 123 | ||
123 | static void sfe4001_fini(struct efx_nic *efx) | 124 | static int sfe4001_poweron(struct efx_nic *efx) |
124 | { | 125 | { |
125 | EFX_INFO(efx, "%s\n", __func__); | 126 | struct i2c_client *hwmon_client = efx->board_info.hwmon_client; |
126 | 127 | struct i2c_client *ioexp_client = efx->board_info.ioexp_client; | |
127 | sfe4001_poweroff(efx); | ||
128 | i2c_unregister_device(efx->board_info.ioexp_client); | ||
129 | i2c_unregister_device(efx->board_info.hwmon_client); | ||
130 | } | ||
131 | |||
132 | /* The P0_EN_3V3X line on SFE4001 boards (from A2 onward) is connected | ||
133 | * to the FLASH_CFG_1 input on the DSP. We must keep it high at power- | ||
134 | * up to allow writing the flash (done through MDIO from userland). | ||
135 | */ | ||
136 | unsigned int sfe4001_phy_flash_cfg; | ||
137 | module_param_named(phy_flash_cfg, sfe4001_phy_flash_cfg, uint, 0444); | ||
138 | MODULE_PARM_DESC(phy_flash_cfg, | ||
139 | "Force PHY to enter flash configuration mode"); | ||
140 | |||
141 | /* This board uses an I2C expander to provider power to the PHY, which needs to | ||
142 | * be turned on before the PHY can be used. | ||
143 | * Context: Process context, rtnl lock held | ||
144 | */ | ||
145 | int sfe4001_init(struct efx_nic *efx) | ||
146 | { | ||
147 | struct i2c_client *hwmon_client, *ioexp_client; | ||
148 | unsigned int i, j; | 128 | unsigned int i, j; |
149 | int rc; | 129 | int rc; |
150 | u8 out; | 130 | u8 out; |
151 | efx_dword_t reg; | 131 | efx_dword_t reg; |
152 | 132 | ||
153 | hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647); | ||
154 | if (!hwmon_client) | ||
155 | return -EIO; | ||
156 | efx->board_info.hwmon_client = hwmon_client; | ||
157 | |||
158 | ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539); | ||
159 | if (!ioexp_client) { | ||
160 | rc = -EIO; | ||
161 | goto fail_hwmon; | ||
162 | } | ||
163 | efx->board_info.ioexp_client = ioexp_client; | ||
164 | |||
165 | /* 10Xpress has fixed-function LED pins, so there is no board-specific | ||
166 | * blink code. */ | ||
167 | efx->board_info.blink = tenxpress_phy_blink; | ||
168 | |||
169 | /* Ensure that XGXS and XAUI SerDes are held in reset */ | 133 | /* Ensure that XGXS and XAUI SerDes are held in reset */ |
170 | EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1, | 134 | EFX_POPULATE_DWORD_7(reg, XX_PWRDNA_EN, 1, |
171 | XX_PWRDNB_EN, 1, | 135 | XX_PWRDNB_EN, 1, |
@@ -177,33 +141,15 @@ int sfe4001_init(struct efx_nic *efx) | |||
177 | falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); | 141 | falcon_xmac_writel(efx, ®, XX_PWR_RST_REG_MAC); |
178 | udelay(10); | 142 | udelay(10); |
179 | 143 | ||
180 | efx->board_info.fini = sfe4001_fini; | ||
181 | |||
182 | /* Set DSP over-temperature alert threshold */ | ||
183 | EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature); | ||
184 | rc = i2c_smbus_write_byte_data(hwmon_client, WLHO, | ||
185 | xgphy_max_temperature); | ||
186 | if (rc) | ||
187 | goto fail_ioexp; | ||
188 | |||
189 | /* Read it back and verify */ | ||
190 | rc = i2c_smbus_read_byte_data(hwmon_client, RLHN); | ||
191 | if (rc < 0) | ||
192 | goto fail_ioexp; | ||
193 | if (rc != xgphy_max_temperature) { | ||
194 | rc = -EFAULT; | ||
195 | goto fail_ioexp; | ||
196 | } | ||
197 | |||
198 | /* Clear any previous over-temperature alert */ | 144 | /* Clear any previous over-temperature alert */ |
199 | rc = i2c_smbus_read_byte_data(hwmon_client, RSL); | 145 | rc = i2c_smbus_read_byte_data(hwmon_client, RSL); |
200 | if (rc < 0) | 146 | if (rc < 0) |
201 | goto fail_ioexp; | 147 | return rc; |
202 | 148 | ||
203 | /* Enable port 0 and port 1 outputs on IO expander */ | 149 | /* Enable port 0 and port 1 outputs on IO expander */ |
204 | rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); | 150 | rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00); |
205 | if (rc) | 151 | if (rc) |
206 | goto fail_ioexp; | 152 | return rc; |
207 | rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, | 153 | rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, |
208 | 0xff & ~(1 << P1_SPARE_LBN)); | 154 | 0xff & ~(1 << P1_SPARE_LBN)); |
209 | if (rc) | 155 | if (rc) |
@@ -231,7 +177,7 @@ int sfe4001_init(struct efx_nic *efx) | |||
231 | out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | | 177 | out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | |
232 | (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | | 178 | (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | |
233 | (1 << P0_X_TRST_LBN)); | 179 | (1 << P0_X_TRST_LBN)); |
234 | if (sfe4001_phy_flash_cfg) | 180 | if (efx->phy_mode & PHY_MODE_SPECIAL) |
235 | out |= 1 << P0_EN_3V3X_LBN; | 181 | out |= 1 << P0_EN_3V3X_LBN; |
236 | 182 | ||
237 | rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); | 183 | rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out); |
@@ -250,9 +196,9 @@ int sfe4001_init(struct efx_nic *efx) | |||
250 | /* In flash config mode, DSP does not turn on AFE, so | 196 | /* In flash config mode, DSP does not turn on AFE, so |
251 | * just wait 1 second. | 197 | * just wait 1 second. |
252 | */ | 198 | */ |
253 | if (sfe4001_phy_flash_cfg) { | 199 | if (efx->phy_mode & PHY_MODE_SPECIAL) { |
254 | schedule_timeout_uninterruptible(HZ); | 200 | schedule_timeout_uninterruptible(HZ); |
255 | goto done; | 201 | return 0; |
256 | } | 202 | } |
257 | 203 | ||
258 | for (j = 0; j < 10; ++j) { | 204 | for (j = 0; j < 10; ++j) { |
@@ -263,23 +209,127 @@ int sfe4001_init(struct efx_nic *efx) | |||
263 | if (rc < 0) | 209 | if (rc < 0) |
264 | goto fail_on; | 210 | goto fail_on; |
265 | if (rc & (1 << P1_AFE_PWD_LBN)) | 211 | if (rc & (1 << P1_AFE_PWD_LBN)) |
266 | goto done; | 212 | return 0; |
267 | } | 213 | } |
268 | } | 214 | } |
269 | 215 | ||
270 | EFX_INFO(efx, "timed out waiting for DSP boot\n"); | 216 | EFX_INFO(efx, "timed out waiting for DSP boot\n"); |
271 | rc = -ETIMEDOUT; | 217 | rc = -ETIMEDOUT; |
272 | goto fail_on; | 218 | fail_on: |
219 | sfe4001_poweroff(efx); | ||
220 | return rc; | ||
221 | } | ||
222 | |||
223 | /* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin | ||
224 | * using the 3V3X output of the IO-expander. Allow the user to set | ||
225 | * this when the device is stopped, and keep it stopped then. | ||
226 | */ | ||
227 | |||
228 | static ssize_t show_phy_flash_cfg(struct device *dev, | ||
229 | struct device_attribute *attr, char *buf) | ||
230 | { | ||
231 | struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); | ||
232 | return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL)); | ||
233 | } | ||
234 | |||
235 | static ssize_t set_phy_flash_cfg(struct device *dev, | ||
236 | struct device_attribute *attr, | ||
237 | const char *buf, size_t count) | ||
238 | { | ||
239 | struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev)); | ||
240 | enum efx_phy_mode old_mode, new_mode; | ||
241 | int err; | ||
242 | |||
243 | rtnl_lock(); | ||
244 | old_mode = efx->phy_mode; | ||
245 | if (count == 0 || *buf == '0') | ||
246 | new_mode = old_mode & ~PHY_MODE_SPECIAL; | ||
247 | else | ||
248 | new_mode = PHY_MODE_SPECIAL; | ||
249 | if (old_mode == new_mode) { | ||
250 | err = 0; | ||
251 | } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) { | ||
252 | err = -EBUSY; | ||
253 | } else { | ||
254 | efx->phy_mode = new_mode; | ||
255 | err = sfe4001_poweron(efx); | ||
256 | efx_reconfigure_port(efx); | ||
257 | } | ||
258 | rtnl_unlock(); | ||
259 | |||
260 | return err ? err : count; | ||
261 | } | ||
262 | |||
263 | static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg); | ||
264 | |||
265 | static void sfe4001_fini(struct efx_nic *efx) | ||
266 | { | ||
267 | EFX_INFO(efx, "%s\n", __func__); | ||
268 | |||
269 | device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); | ||
270 | sfe4001_poweroff(efx); | ||
271 | i2c_unregister_device(efx->board_info.ioexp_client); | ||
272 | i2c_unregister_device(efx->board_info.hwmon_client); | ||
273 | } | ||
274 | |||
275 | /* This board uses an I2C expander to provider power to the PHY, which needs to | ||
276 | * be turned on before the PHY can be used. | ||
277 | * Context: Process context, rtnl lock held | ||
278 | */ | ||
279 | int sfe4001_init(struct efx_nic *efx) | ||
280 | { | ||
281 | struct i2c_client *hwmon_client; | ||
282 | int rc; | ||
283 | |||
284 | hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647); | ||
285 | if (!hwmon_client) | ||
286 | return -EIO; | ||
287 | efx->board_info.hwmon_client = hwmon_client; | ||
288 | |||
289 | /* Set DSP over-temperature alert threshold */ | ||
290 | EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature); | ||
291 | rc = i2c_smbus_write_byte_data(hwmon_client, WLHO, | ||
292 | xgphy_max_temperature); | ||
293 | if (rc) | ||
294 | goto fail_ioexp; | ||
295 | |||
296 | /* Read it back and verify */ | ||
297 | rc = i2c_smbus_read_byte_data(hwmon_client, RLHN); | ||
298 | if (rc < 0) | ||
299 | goto fail_ioexp; | ||
300 | if (rc != xgphy_max_temperature) { | ||
301 | rc = -EFAULT; | ||
302 | goto fail_ioexp; | ||
303 | } | ||
304 | |||
305 | efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539); | ||
306 | if (!efx->board_info.ioexp_client) { | ||
307 | rc = -EIO; | ||
308 | goto fail_hwmon; | ||
309 | } | ||
310 | |||
311 | /* 10Xpress has fixed-function LED pins, so there is no board-specific | ||
312 | * blink code. */ | ||
313 | efx->board_info.blink = tenxpress_phy_blink; | ||
314 | |||
315 | efx->board_info.fini = sfe4001_fini; | ||
316 | |||
317 | rc = sfe4001_poweron(efx); | ||
318 | if (rc) | ||
319 | goto fail_ioexp; | ||
320 | |||
321 | rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg); | ||
322 | if (rc) | ||
323 | goto fail_on; | ||
273 | 324 | ||
274 | done: | ||
275 | EFX_INFO(efx, "PHY is powered on\n"); | 325 | EFX_INFO(efx, "PHY is powered on\n"); |
276 | return 0; | 326 | return 0; |
277 | 327 | ||
278 | fail_on: | 328 | fail_on: |
279 | sfe4001_poweroff(efx); | 329 | sfe4001_poweroff(efx); |
280 | fail_ioexp: | 330 | fail_ioexp: |
281 | i2c_unregister_device(ioexp_client); | 331 | i2c_unregister_device(efx->board_info.ioexp_client); |
282 | fail_hwmon: | 332 | fail_hwmon: |
283 | i2c_unregister_device(hwmon_client); | 333 | i2c_unregister_device(hwmon_client); |
284 | return rc; | 334 | return rc; |
285 | } | 335 | } |