diff options
author | Matthew Slattery <mslattery@solarflare.com> | 2009-12-23 08:48:04 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-23 22:09:06 -0500 |
commit | 0d83b2f64c330ee3892cb3117ac5d56e97185ecf (patch) | |
tree | 40d511c2dd7d565330771a82165b6a26dfb8928c /drivers/net/sfc/qt202x_phy.c | |
parent | 17d6aeafe9d8268612d91edc0102659edb382282 (diff) |
sfc: QT2025C: Switch into self-configure mode when not in loopback
The PHY boots in a mode which is not necessarily optimal. This change
switches it to self-configure mode (except when in loopback, which
won't work in that mode if an SFP+ module is not present) by rebooting
the PHY's microcontroller, and replicating the sequence of configuration
writes from the boot EEPROM with the appropriate changes.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc/qt202x_phy.c')
-rw-r--r-- | drivers/net/sfc/qt202x_phy.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c index e4590fb528a1..0cd6eed02069 100644 --- a/drivers/net/sfc/qt202x_phy.c +++ b/drivers/net/sfc/qt202x_phy.c | |||
@@ -33,6 +33,9 @@ | |||
33 | #define PCS_FW_HEARTBEAT_REG 0xd7ee | 33 | #define PCS_FW_HEARTBEAT_REG 0xd7ee |
34 | #define PCS_FW_HEARTB_LBN 0 | 34 | #define PCS_FW_HEARTB_LBN 0 |
35 | #define PCS_FW_HEARTB_WIDTH 8 | 35 | #define PCS_FW_HEARTB_WIDTH 8 |
36 | #define PCS_FW_PRODUCT_CODE_1 0xd7f0 | ||
37 | #define PCS_FW_VERSION_1 0xd7f3 | ||
38 | #define PCS_FW_BUILD_1 0xd7f6 | ||
36 | #define PCS_UC8051_STATUS_REG 0xd7fd | 39 | #define PCS_UC8051_STATUS_REG 0xd7fd |
37 | #define PCS_UC_STATUS_LBN 0 | 40 | #define PCS_UC_STATUS_LBN 0 |
38 | #define PCS_UC_STATUS_WIDTH 8 | 41 | #define PCS_UC_STATUS_WIDTH 8 |
@@ -54,6 +57,7 @@ struct qt202x_phy_data { | |||
54 | enum efx_phy_mode phy_mode; | 57 | enum efx_phy_mode phy_mode; |
55 | bool bug17190_in_bad_state; | 58 | bool bug17190_in_bad_state; |
56 | unsigned long bug17190_timer; | 59 | unsigned long bug17190_timer; |
60 | u32 firmware_ver; | ||
57 | }; | 61 | }; |
58 | 62 | ||
59 | #define QT2022C2_MAX_RESET_TIME 500 | 63 | #define QT2022C2_MAX_RESET_TIME 500 |
@@ -100,6 +104,25 @@ static int qt2025c_wait_reset(struct efx_nic *efx) | |||
100 | return 0; | 104 | return 0; |
101 | } | 105 | } |
102 | 106 | ||
107 | static void qt2025c_firmware_id(struct efx_nic *efx) | ||
108 | { | ||
109 | struct qt202x_phy_data *phy_data = efx->phy_data; | ||
110 | u8 firmware_id[9]; | ||
111 | size_t i; | ||
112 | |||
113 | for (i = 0; i < sizeof(firmware_id); i++) | ||
114 | firmware_id[i] = efx_mdio_read(efx, MDIO_MMD_PCS, | ||
115 | PCS_FW_PRODUCT_CODE_1 + i); | ||
116 | EFX_INFO(efx, "QT2025C firmware %xr%d v%d.%d.%d.%d [20%02d-%02d-%02d]\n", | ||
117 | (firmware_id[0] << 8) | firmware_id[1], firmware_id[2], | ||
118 | firmware_id[3] >> 4, firmware_id[3] & 0xf, | ||
119 | firmware_id[4], firmware_id[5], | ||
120 | firmware_id[6], firmware_id[7], firmware_id[8]); | ||
121 | phy_data->firmware_ver = ((firmware_id[3] & 0xf0) << 20) | | ||
122 | ((firmware_id[3] & 0x0f) << 16) | | ||
123 | (firmware_id[4] << 8) | firmware_id[5]; | ||
124 | } | ||
125 | |||
103 | static void qt2025c_bug17190_workaround(struct efx_nic *efx) | 126 | static void qt2025c_bug17190_workaround(struct efx_nic *efx) |
104 | { | 127 | { |
105 | struct qt202x_phy_data *phy_data = efx->phy_data; | 128 | struct qt202x_phy_data *phy_data = efx->phy_data; |
@@ -133,6 +156,95 @@ static void qt2025c_bug17190_workaround(struct efx_nic *efx) | |||
133 | } | 156 | } |
134 | } | 157 | } |
135 | 158 | ||
159 | static int qt2025c_select_phy_mode(struct efx_nic *efx) | ||
160 | { | ||
161 | struct qt202x_phy_data *phy_data = efx->phy_data; | ||
162 | struct falcon_board *board = falcon_board(efx); | ||
163 | int reg, rc, i; | ||
164 | uint16_t phy_op_mode; | ||
165 | |||
166 | /* Only 2.0.1.0+ PHY firmware supports the more optimal SFP+ | ||
167 | * Self-Configure mode. Don't attempt any switching if we encounter | ||
168 | * older firmware. */ | ||
169 | if (phy_data->firmware_ver < 0x02000100) | ||
170 | return 0; | ||
171 | |||
172 | /* In general we will get optimal behaviour in "SFP+ Self-Configure" | ||
173 | * mode; however, that powers down most of the PHY when no module is | ||
174 | * present, so we must use a different mode (any fixed mode will do) | ||
175 | * to be sure that loopbacks will work. */ | ||
176 | phy_op_mode = (efx->loopback_mode == LOOPBACK_NONE) ? 0x0038 : 0x0020; | ||
177 | |||
178 | /* Only change mode if really necessary */ | ||
179 | reg = efx_mdio_read(efx, 1, 0xc319); | ||
180 | if ((reg & 0x0038) == phy_op_mode) | ||
181 | return 0; | ||
182 | EFX_LOG(efx, "Switching PHY to mode 0x%04x\n", phy_op_mode); | ||
183 | |||
184 | /* This sequence replicates the register writes configured in the boot | ||
185 | * EEPROM (including the differences between board revisions), except | ||
186 | * that the operating mode is changed, and the PHY is prevented from | ||
187 | * unnecessarily reloading the main firmware image again. */ | ||
188 | efx_mdio_write(efx, 1, 0xc300, 0x0000); | ||
189 | /* (Note: this portion of the boot EEPROM sequence, which bit-bashes 9 | ||
190 | * STOPs onto the firmware/module I2C bus to reset it, varies across | ||
191 | * board revisions, as the bus is connected to different GPIO/LED | ||
192 | * outputs on the PHY.) */ | ||
193 | if (board->major == 0 && board->minor < 2) { | ||
194 | efx_mdio_write(efx, 1, 0xc303, 0x4498); | ||
195 | for (i = 0; i < 9; i++) { | ||
196 | efx_mdio_write(efx, 1, 0xc303, 0x4488); | ||
197 | efx_mdio_write(efx, 1, 0xc303, 0x4480); | ||
198 | efx_mdio_write(efx, 1, 0xc303, 0x4490); | ||
199 | efx_mdio_write(efx, 1, 0xc303, 0x4498); | ||
200 | } | ||
201 | } else { | ||
202 | efx_mdio_write(efx, 1, 0xc303, 0x0920); | ||
203 | efx_mdio_write(efx, 1, 0xd008, 0x0004); | ||
204 | for (i = 0; i < 9; i++) { | ||
205 | efx_mdio_write(efx, 1, 0xc303, 0x0900); | ||
206 | efx_mdio_write(efx, 1, 0xd008, 0x0005); | ||
207 | efx_mdio_write(efx, 1, 0xc303, 0x0920); | ||
208 | efx_mdio_write(efx, 1, 0xd008, 0x0004); | ||
209 | } | ||
210 | efx_mdio_write(efx, 1, 0xc303, 0x4900); | ||
211 | } | ||
212 | efx_mdio_write(efx, 1, 0xc303, 0x4900); | ||
213 | efx_mdio_write(efx, 1, 0xc302, 0x0004); | ||
214 | efx_mdio_write(efx, 1, 0xc316, 0x0013); | ||
215 | efx_mdio_write(efx, 1, 0xc318, 0x0054); | ||
216 | efx_mdio_write(efx, 1, 0xc319, phy_op_mode); | ||
217 | efx_mdio_write(efx, 1, 0xc31a, 0x0098); | ||
218 | efx_mdio_write(efx, 3, 0x0026, 0x0e00); | ||
219 | efx_mdio_write(efx, 3, 0x0027, 0x0013); | ||
220 | efx_mdio_write(efx, 3, 0x0028, 0xa528); | ||
221 | efx_mdio_write(efx, 1, 0xd006, 0x000a); | ||
222 | efx_mdio_write(efx, 1, 0xd007, 0x0009); | ||
223 | efx_mdio_write(efx, 1, 0xd008, 0x0004); | ||
224 | /* This additional write is not present in the boot EEPROM. It | ||
225 | * prevents the PHY's internal boot ROM doing another pointless (and | ||
226 | * slow) reload of the firmware image (the microcontroller's code | ||
227 | * memory is not affected by the microcontroller reset). */ | ||
228 | efx_mdio_write(efx, 1, 0xc317, 0x00ff); | ||
229 | efx_mdio_write(efx, 1, 0xc300, 0x0002); | ||
230 | msleep(20); | ||
231 | |||
232 | /* Restart microcontroller execution from RAM */ | ||
233 | efx_mdio_write(efx, 3, 0xe854, 0x00c0); | ||
234 | efx_mdio_write(efx, 3, 0xe854, 0x0040); | ||
235 | msleep(50); | ||
236 | |||
237 | /* Wait for the microcontroller to be ready again */ | ||
238 | rc = qt2025c_wait_reset(efx); | ||
239 | if (rc < 0) { | ||
240 | EFX_ERR(efx, "PHY microcontroller reset during mode switch " | ||
241 | "timed out\n"); | ||
242 | return rc; | ||
243 | } | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
136 | static int qt202x_reset_phy(struct efx_nic *efx) | 248 | static int qt202x_reset_phy(struct efx_nic *efx) |
137 | { | 249 | { |
138 | int rc; | 250 | int rc; |
@@ -206,6 +318,9 @@ static int qt202x_phy_init(struct efx_nic *efx) | |||
206 | devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), | 318 | devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid), |
207 | efx_mdio_id_rev(devid)); | 319 | efx_mdio_id_rev(devid)); |
208 | 320 | ||
321 | if (efx->phy_type == PHY_TYPE_QT2025C) | ||
322 | qt2025c_firmware_id(efx); | ||
323 | |||
209 | return 0; | 324 | return 0; |
210 | } | 325 | } |
211 | 326 | ||
@@ -234,6 +349,10 @@ static int qt202x_phy_reconfigure(struct efx_nic *efx) | |||
234 | struct qt202x_phy_data *phy_data = efx->phy_data; | 349 | struct qt202x_phy_data *phy_data = efx->phy_data; |
235 | 350 | ||
236 | if (efx->phy_type == PHY_TYPE_QT2025C) { | 351 | if (efx->phy_type == PHY_TYPE_QT2025C) { |
352 | int rc = qt2025c_select_phy_mode(efx); | ||
353 | if (rc) | ||
354 | return rc; | ||
355 | |||
237 | /* There are several different register bits which can | 356 | /* There are several different register bits which can |
238 | * disable TX (and save power) on direct-attach cables | 357 | * disable TX (and save power) on direct-attach cables |
239 | * or optical transceivers, varying somewhat between | 358 | * or optical transceivers, varying somewhat between |