]>
Commit | Line | Data |
---|---|---|
8ceee660 BH |
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 | } |