diff options
Diffstat (limited to 'drivers/net/sfc/mdio_10g.c')
-rw-r--r-- | drivers/net/sfc/mdio_10g.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c new file mode 100644 index 000000000000..dc06bb0aa575 --- /dev/null +++ b/drivers/net/sfc/mdio_10g.c | |||
@@ -0,0 +1,282 @@ | |||
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 | * Useful functions for working with MDIO clause 45 PHYs | ||
11 | */ | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/ethtool.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include "net_driver.h" | ||
16 | #include "mdio_10g.h" | ||
17 | #include "boards.h" | ||
18 | |||
19 | int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd, | ||
20 | int spins, int spintime) | ||
21 | { | ||
22 | u32 ctrl; | ||
23 | int phy_id = port->mii.phy_id; | ||
24 | |||
25 | /* Catch callers passing values in the wrong units (or just silly) */ | ||
26 | EFX_BUG_ON_PARANOID(spins * spintime >= 5000); | ||
27 | |||
28 | mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1, | ||
29 | (1 << MDIO_MMDREG_CTRL1_RESET_LBN)); | ||
30 | /* Wait for the reset bit to clear. */ | ||
31 | do { | ||
32 | msleep(spintime); | ||
33 | ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1); | ||
34 | spins--; | ||
35 | |||
36 | } while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))); | ||
37 | |||
38 | return spins ? spins : -ETIMEDOUT; | ||
39 | } | ||
40 | |||
41 | static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, | ||
42 | int fault_fatal) | ||
43 | { | ||
44 | int status; | ||
45 | int phy_id = efx->mii.phy_id; | ||
46 | |||
47 | /* Read MMD STATUS2 to check it is responding. */ | ||
48 | status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2); | ||
49 | if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & | ||
50 | ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != | ||
51 | MDIO_MMDREG_STAT2_PRESENT_VAL) { | ||
52 | EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd); | ||
53 | return -EIO; | ||
54 | } | ||
55 | |||
56 | /* Read MMD STATUS 1 to check for fault. */ | ||
57 | status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1); | ||
58 | if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) { | ||
59 | if (fault_fatal) { | ||
60 | EFX_ERR(efx, "PHY MMD %d reporting fatal" | ||
61 | " fault: status %x\n", mmd, status); | ||
62 | return -EIO; | ||
63 | } else { | ||
64 | EFX_LOG(efx, "PHY MMD %d reporting status" | ||
65 | " %x (expected)\n", mmd, status); | ||
66 | } | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /* This ought to be ridiculous overkill. We expect it to fail rarely */ | ||
72 | #define MDIO45_RESET_TIME 1000 /* ms */ | ||
73 | #define MDIO45_RESET_ITERS 100 | ||
74 | |||
75 | int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, | ||
76 | unsigned int mmd_mask) | ||
77 | { | ||
78 | const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; | ||
79 | int tries = MDIO45_RESET_ITERS; | ||
80 | int rc = 0; | ||
81 | int in_reset; | ||
82 | |||
83 | while (tries) { | ||
84 | int mask = mmd_mask; | ||
85 | int mmd = 0; | ||
86 | int stat; | ||
87 | in_reset = 0; | ||
88 | while (mask) { | ||
89 | if (mask & 1) { | ||
90 | stat = mdio_clause45_read(efx, | ||
91 | efx->mii.phy_id, | ||
92 | mmd, | ||
93 | MDIO_MMDREG_CTRL1); | ||
94 | if (stat < 0) { | ||
95 | EFX_ERR(efx, "failed to read status of" | ||
96 | " MMD %d\n", mmd); | ||
97 | return -EIO; | ||
98 | } | ||
99 | if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) | ||
100 | in_reset |= (1 << mmd); | ||
101 | } | ||
102 | mask = mask >> 1; | ||
103 | mmd++; | ||
104 | } | ||
105 | if (!in_reset) | ||
106 | break; | ||
107 | tries--; | ||
108 | msleep(spintime); | ||
109 | } | ||
110 | if (in_reset != 0) { | ||
111 | EFX_ERR(efx, "not all MMDs came out of reset in time." | ||
112 | " MMDs still in reset: %x\n", in_reset); | ||
113 | rc = -ETIMEDOUT; | ||
114 | } | ||
115 | return rc; | ||
116 | } | ||
117 | |||
118 | int mdio_clause45_check_mmds(struct efx_nic *efx, | ||
119 | unsigned int mmd_mask, unsigned int fatal_mask) | ||
120 | { | ||
121 | int devices, mmd = 0; | ||
122 | int probe_mmd; | ||
123 | |||
124 | /* Historically we have probed the PHYXS to find out what devices are | ||
125 | * present,but that doesn't work so well if the PHYXS isn't expected | ||
126 | * to exist, if so just find the first item in the list supplied. */ | ||
127 | probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS : | ||
128 | __ffs(mmd_mask); | ||
129 | devices = mdio_clause45_read(efx, efx->mii.phy_id, | ||
130 | probe_mmd, MDIO_MMDREG_DEVS0); | ||
131 | |||
132 | /* Check all the expected MMDs are present */ | ||
133 | if (devices < 0) { | ||
134 | EFX_ERR(efx, "failed to read devices present\n"); | ||
135 | return -EIO; | ||
136 | } | ||
137 | if ((devices & mmd_mask) != mmd_mask) { | ||
138 | EFX_ERR(efx, "required MMDs not present: got %x, " | ||
139 | "wanted %x\n", devices, mmd_mask); | ||
140 | return -ENODEV; | ||
141 | } | ||
142 | EFX_TRACE(efx, "Devices present: %x\n", devices); | ||
143 | |||
144 | /* Check all required MMDs are responding and happy. */ | ||
145 | while (mmd_mask) { | ||
146 | if (mmd_mask & 1) { | ||
147 | int fault_fatal = fatal_mask & 1; | ||
148 | if (mdio_clause45_check_mmd(efx, mmd, fault_fatal)) | ||
149 | return -EIO; | ||
150 | } | ||
151 | mmd_mask = mmd_mask >> 1; | ||
152 | fatal_mask = fatal_mask >> 1; | ||
153 | mmd++; | ||
154 | } | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | int mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) | ||
160 | { | ||
161 | int phy_id = efx->mii.phy_id; | ||
162 | int status; | ||
163 | int ok = 1; | ||
164 | int mmd = 0; | ||
165 | int good; | ||
166 | |||
167 | while (mmd_mask) { | ||
168 | if (mmd_mask & 1) { | ||
169 | /* Double reads because link state is latched, and a | ||
170 | * read moves the current state into the register */ | ||
171 | status = mdio_clause45_read(efx, phy_id, | ||
172 | mmd, MDIO_MMDREG_STAT1); | ||
173 | status = mdio_clause45_read(efx, phy_id, | ||
174 | mmd, MDIO_MMDREG_STAT1); | ||
175 | |||
176 | good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN); | ||
177 | ok = ok && good; | ||
178 | } | ||
179 | mmd_mask = (mmd_mask >> 1); | ||
180 | mmd++; | ||
181 | } | ||
182 | return ok; | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. | ||
187 | * @efx: Efx NIC | ||
188 | * @ecmd: Buffer for settings | ||
189 | * | ||
190 | * On return the 'port', 'speed', 'supported' and 'advertising' fields of | ||
191 | * ecmd have been filled out based on the PMA type. | ||
192 | */ | ||
193 | void mdio_clause45_get_settings(struct efx_nic *efx, | ||
194 | struct ethtool_cmd *ecmd) | ||
195 | { | ||
196 | int pma_type; | ||
197 | |||
198 | /* If no PMA is present we are presumably talking something XAUI-ish | ||
199 | * like CX4. Which we report as FIBRE (see below) */ | ||
200 | if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) { | ||
201 | ecmd->speed = SPEED_10000; | ||
202 | ecmd->port = PORT_FIBRE; | ||
203 | ecmd->supported = SUPPORTED_FIBRE; | ||
204 | ecmd->advertising = ADVERTISED_FIBRE; | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | pma_type = mdio_clause45_read(efx, efx->mii.phy_id, | ||
209 | MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2); | ||
210 | pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK; | ||
211 | |||
212 | switch (pma_type) { | ||
213 | /* We represent CX4 as fibre in the absence of anything | ||
214 | better. */ | ||
215 | case MDIO_PMAPMD_CTRL2_10G_CX4: | ||
216 | ecmd->speed = SPEED_10000; | ||
217 | ecmd->port = PORT_FIBRE; | ||
218 | ecmd->supported = SUPPORTED_FIBRE; | ||
219 | ecmd->advertising = ADVERTISED_FIBRE; | ||
220 | break; | ||
221 | /* 10G Base-T */ | ||
222 | case MDIO_PMAPMD_CTRL2_10G_BT: | ||
223 | ecmd->speed = SPEED_10000; | ||
224 | ecmd->port = PORT_TP; | ||
225 | ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full; | ||
226 | ecmd->advertising = (ADVERTISED_FIBRE | ||
227 | | ADVERTISED_10000baseT_Full); | ||
228 | break; | ||
229 | case MDIO_PMAPMD_CTRL2_1G_BT: | ||
230 | ecmd->speed = SPEED_1000; | ||
231 | ecmd->port = PORT_TP; | ||
232 | ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full; | ||
233 | ecmd->advertising = (ADVERTISED_FIBRE | ||
234 | | ADVERTISED_1000baseT_Full); | ||
235 | break; | ||
236 | case MDIO_PMAPMD_CTRL2_100_BT: | ||
237 | ecmd->speed = SPEED_100; | ||
238 | ecmd->port = PORT_TP; | ||
239 | ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full; | ||
240 | ecmd->advertising = (ADVERTISED_FIBRE | ||
241 | | ADVERTISED_100baseT_Full); | ||
242 | break; | ||
243 | case MDIO_PMAPMD_CTRL2_10_BT: | ||
244 | ecmd->speed = SPEED_10; | ||
245 | ecmd->port = PORT_TP; | ||
246 | ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full; | ||
247 | ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full; | ||
248 | break; | ||
249 | /* All the other defined modes are flavours of | ||
250 | * 10G optical */ | ||
251 | default: | ||
252 | ecmd->speed = SPEED_10000; | ||
253 | ecmd->port = PORT_FIBRE; | ||
254 | ecmd->supported = SUPPORTED_FIBRE; | ||
255 | ecmd->advertising = ADVERTISED_FIBRE; | ||
256 | break; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. | ||
262 | * @efx: Efx NIC | ||
263 | * @ecmd: New settings | ||
264 | * | ||
265 | * Currently this just enforces that we are _not_ changing the | ||
266 | * 'port', 'speed', 'supported' or 'advertising' settings as these | ||
267 | * cannot be changed on any currently supported PHY. | ||
268 | */ | ||
269 | int mdio_clause45_set_settings(struct efx_nic *efx, | ||
270 | struct ethtool_cmd *ecmd) | ||
271 | { | ||
272 | struct ethtool_cmd tmpcmd; | ||
273 | mdio_clause45_get_settings(efx, &tmpcmd); | ||
274 | /* None of the current PHYs support more than one mode | ||
275 | * of operation (and only 10GBT ever will), so keep things | ||
276 | * simple for now */ | ||
277 | if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) && | ||
278 | (ecmd->supported == tmpcmd.supported) && | ||
279 | (ecmd->advertising == tmpcmd.advertising)) | ||
280 | return 0; | ||
281 | return -EOPNOTSUPP; | ||
282 | } | ||