]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
6d91782f AL |
2 | /* |
3 | * Marvell 88E6xxx SERDES manipulation, via SMI bus | |
4 | * | |
5 | * Copyright (c) 2008 Marvell Semiconductor | |
6 | * | |
7 | * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> | |
6d91782f AL |
8 | */ |
9 | ||
efd1ba6a AL |
10 | #include <linux/interrupt.h> |
11 | #include <linux/irqdomain.h> | |
6d91782f AL |
12 | #include <linux/mii.h> |
13 | ||
4d5f2ba7 | 14 | #include "chip.h" |
6335e9f2 | 15 | #include "global2.h" |
6d91782f AL |
16 | #include "phy.h" |
17 | #include "port.h" | |
18 | #include "serdes.h" | |
19 | ||
20 | static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, | |
21 | u16 *val) | |
22 | { | |
23 | return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, | |
24 | MV88E6352_SERDES_PAGE_FIBER, | |
25 | reg, val); | |
26 | } | |
27 | ||
28 | static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, | |
29 | u16 val) | |
30 | { | |
31 | return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, | |
32 | MV88E6352_SERDES_PAGE_FIBER, | |
33 | reg, val); | |
34 | } | |
35 | ||
e6891c76 AL |
36 | static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, |
37 | int lane, int device, int reg, u16 *val) | |
38 | { | |
39 | int reg_c45 = MII_ADDR_C45 | device << 16 | reg; | |
40 | ||
41 | return mv88e6xxx_phy_read(chip, lane, reg_c45, val); | |
42 | } | |
43 | ||
44 | static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, | |
45 | int lane, int device, int reg, u16 val) | |
46 | { | |
47 | int reg_c45 = MII_ADDR_C45 | device << 16 | reg; | |
48 | ||
49 | return mv88e6xxx_phy_write(chip, lane, reg_c45, val); | |
50 | } | |
51 | ||
6d91782f AL |
52 | static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) |
53 | { | |
54 | u16 val, new_val; | |
55 | int err; | |
56 | ||
57 | err = mv88e6352_serdes_read(chip, MII_BMCR, &val); | |
58 | if (err) | |
59 | return err; | |
60 | ||
61 | if (on) | |
62 | new_val = val & ~BMCR_PDOWN; | |
63 | else | |
64 | new_val = val | BMCR_PDOWN; | |
65 | ||
66 | if (val != new_val) | |
67 | err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); | |
68 | ||
69 | return err; | |
70 | } | |
71 | ||
eb755c3f | 72 | static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) |
6d91782f | 73 | { |
2d2e1dd2 | 74 | u8 cmode = chip->ports[port].cmode; |
6d91782f | 75 | |
5f83dc93 VD |
76 | if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || |
77 | (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || | |
eb755c3f | 78 | (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) |
b1312b85 | 79 | return true; |
eb755c3f | 80 | |
b1312b85 | 81 | return false; |
eb755c3f AL |
82 | } |
83 | ||
84 | int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) | |
85 | { | |
86 | int err; | |
87 | ||
88 | if (mv88e6352_port_has_serdes(chip, port)) { | |
6d91782f AL |
89 | err = mv88e6352_serdes_power_set(chip, on); |
90 | if (err < 0) | |
91 | return err; | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
6335e9f2 | 96 | |
cda9f4aa AL |
97 | struct mv88e6352_serdes_hw_stat { |
98 | char string[ETH_GSTRING_LEN]; | |
99 | int sizeof_stat; | |
100 | int reg; | |
101 | }; | |
102 | ||
103 | static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { | |
104 | { "serdes_fibre_rx_error", 16, 21 }, | |
105 | { "serdes_PRBS_error", 32, 24 }, | |
106 | }; | |
107 | ||
108 | int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) | |
109 | { | |
110 | if (mv88e6352_port_has_serdes(chip, port)) | |
111 | return ARRAY_SIZE(mv88e6352_serdes_hw_stats); | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
65f60e45 AL |
116 | int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, |
117 | int port, uint8_t *data) | |
cda9f4aa AL |
118 | { |
119 | struct mv88e6352_serdes_hw_stat *stat; | |
120 | int i; | |
121 | ||
122 | if (!mv88e6352_port_has_serdes(chip, port)) | |
65f60e45 | 123 | return 0; |
cda9f4aa AL |
124 | |
125 | for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { | |
126 | stat = &mv88e6352_serdes_hw_stats[i]; | |
127 | memcpy(data + i * ETH_GSTRING_LEN, stat->string, | |
128 | ETH_GSTRING_LEN); | |
129 | } | |
65f60e45 | 130 | return ARRAY_SIZE(mv88e6352_serdes_hw_stats); |
cda9f4aa AL |
131 | } |
132 | ||
133 | static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, | |
134 | struct mv88e6352_serdes_hw_stat *stat) | |
135 | { | |
136 | u64 val = 0; | |
137 | u16 reg; | |
138 | int err; | |
139 | ||
140 | err = mv88e6352_serdes_read(chip, stat->reg, ®); | |
141 | if (err) { | |
142 | dev_err(chip->dev, "failed to read statistic\n"); | |
143 | return 0; | |
144 | } | |
145 | ||
146 | val = reg; | |
147 | ||
148 | if (stat->sizeof_stat == 32) { | |
149 | err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); | |
150 | if (err) { | |
151 | dev_err(chip->dev, "failed to read statistic\n"); | |
152 | return 0; | |
153 | } | |
154 | val = val << 16 | reg; | |
155 | } | |
156 | ||
157 | return val; | |
158 | } | |
159 | ||
65f60e45 AL |
160 | int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, |
161 | uint64_t *data) | |
cda9f4aa AL |
162 | { |
163 | struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; | |
164 | struct mv88e6352_serdes_hw_stat *stat; | |
165 | u64 value; | |
166 | int i; | |
167 | ||
168 | if (!mv88e6352_port_has_serdes(chip, port)) | |
65f60e45 | 169 | return 0; |
cda9f4aa AL |
170 | |
171 | BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > | |
172 | ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); | |
173 | ||
174 | for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { | |
175 | stat = &mv88e6352_serdes_hw_stats[i]; | |
176 | value = mv88e6352_serdes_get_stat(chip, stat); | |
177 | mv88e6xxx_port->serdes_stats[i] += value; | |
178 | data[i] = mv88e6xxx_port->serdes_stats[i]; | |
179 | } | |
65f60e45 AL |
180 | |
181 | return ARRAY_SIZE(mv88e6352_serdes_hw_stats); | |
cda9f4aa AL |
182 | } |
183 | ||
4382172f AL |
184 | static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) |
185 | { | |
186 | struct dsa_switch *ds = chip->ds; | |
187 | u16 status; | |
188 | bool up; | |
189 | ||
190 | mv88e6352_serdes_read(chip, MII_BMSR, &status); | |
191 | ||
192 | /* Status must be read twice in order to give the current link | |
193 | * status. Otherwise the change in link status since the last | |
194 | * read of the register is returned. | |
195 | */ | |
196 | mv88e6352_serdes_read(chip, MII_BMSR, &status); | |
197 | ||
198 | up = status & BMSR_LSTATUS; | |
199 | ||
200 | dsa_port_phylink_mac_change(ds, port, up); | |
201 | } | |
202 | ||
203 | static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id) | |
204 | { | |
205 | struct mv88e6xxx_port *port = dev_id; | |
206 | struct mv88e6xxx_chip *chip = port->chip; | |
207 | irqreturn_t ret = IRQ_NONE; | |
208 | u16 status; | |
209 | int err; | |
210 | ||
211 | mutex_lock(&chip->reg_lock); | |
212 | ||
213 | err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status); | |
214 | if (err) | |
215 | goto out; | |
216 | ||
217 | if (status & MV88E6352_SERDES_INT_LINK_CHANGE) { | |
218 | ret = IRQ_HANDLED; | |
219 | mv88e6352_serdes_irq_link(chip, port->port); | |
220 | } | |
221 | out: | |
222 | mutex_unlock(&chip->reg_lock); | |
223 | ||
224 | return ret; | |
225 | } | |
226 | ||
227 | static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip) | |
228 | { | |
229 | return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, | |
230 | MV88E6352_SERDES_INT_LINK_CHANGE); | |
231 | } | |
232 | ||
233 | static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip) | |
234 | { | |
235 | return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0); | |
236 | } | |
237 | ||
238 | int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) | |
239 | { | |
240 | int err; | |
241 | ||
242 | if (!mv88e6352_port_has_serdes(chip, port)) | |
243 | return 0; | |
244 | ||
245 | chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, | |
246 | MV88E6352_SERDES_IRQ); | |
247 | if (chip->ports[port].serdes_irq < 0) { | |
248 | dev_err(chip->dev, "Unable to map SERDES irq: %d\n", | |
249 | chip->ports[port].serdes_irq); | |
250 | return chip->ports[port].serdes_irq; | |
251 | } | |
252 | ||
253 | /* Requesting the IRQ will trigger irq callbacks. So we cannot | |
254 | * hold the reg_lock. | |
255 | */ | |
256 | mutex_unlock(&chip->reg_lock); | |
257 | err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, | |
258 | mv88e6352_serdes_thread_fn, | |
259 | IRQF_ONESHOT, "mv88e6xxx-serdes", | |
260 | &chip->ports[port]); | |
261 | mutex_lock(&chip->reg_lock); | |
262 | ||
263 | if (err) { | |
264 | dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", | |
265 | err); | |
266 | return err; | |
267 | } | |
268 | ||
269 | return mv88e6352_serdes_irq_enable(chip); | |
270 | } | |
271 | ||
272 | void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) | |
273 | { | |
274 | if (!mv88e6352_port_has_serdes(chip, port)) | |
275 | return; | |
276 | ||
277 | mv88e6352_serdes_irq_disable(chip); | |
278 | ||
279 | /* Freeing the IRQ will trigger irq callbacks. So we cannot | |
280 | * hold the reg_lock. | |
281 | */ | |
282 | mutex_unlock(&chip->reg_lock); | |
283 | free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); | |
284 | mutex_lock(&chip->reg_lock); | |
285 | ||
286 | chip->ports[port].serdes_irq = 0; | |
287 | } | |
288 | ||
07ffbd74 AL |
289 | /* Return the SERDES lane address a port is using. Only Ports 9 and 10 |
290 | * have SERDES lanes. Returns -ENODEV if a port does not have a lane. | |
291 | */ | |
292 | static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) | |
293 | { | |
2d2e1dd2 | 294 | u8 cmode = chip->ports[port].cmode; |
07ffbd74 AL |
295 | |
296 | switch (port) { | |
297 | case 9: | |
298 | if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
299 | cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
300 | cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) | |
301 | return MV88E6390_PORT9_LANE0; | |
302 | return -ENODEV; | |
303 | case 10: | |
304 | if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
305 | cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
306 | cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) | |
307 | return MV88E6390_PORT10_LANE0; | |
308 | return -ENODEV; | |
309 | default: | |
310 | return -ENODEV; | |
311 | } | |
312 | } | |
313 | ||
a8c01c0d AL |
314 | /* Return the SERDES lane address a port is using. Ports 9 and 10 can |
315 | * use multiple lanes. If so, return the first lane the port uses. | |
316 | * Returns -ENODEV if a port does not have a lane. | |
317 | */ | |
734447d4 | 318 | int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) |
a8c01c0d AL |
319 | { |
320 | u8 cmode_port9, cmode_port10, cmode_port; | |
a8c01c0d | 321 | |
2d2e1dd2 AL |
322 | cmode_port9 = chip->ports[9].cmode; |
323 | cmode_port10 = chip->ports[10].cmode; | |
324 | cmode_port = chip->ports[port].cmode; | |
a8c01c0d AL |
325 | |
326 | switch (port) { | |
327 | case 2: | |
328 | if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
329 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
330 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) | |
331 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
332 | return MV88E6390_PORT9_LANE1; | |
333 | return -ENODEV; | |
334 | case 3: | |
335 | if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
336 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
337 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
338 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
339 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
340 | return MV88E6390_PORT9_LANE2; | |
341 | return -ENODEV; | |
342 | case 4: | |
343 | if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
344 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
345 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
346 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
347 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
348 | return MV88E6390_PORT9_LANE3; | |
349 | return -ENODEV; | |
350 | case 5: | |
351 | if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
352 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
353 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) | |
354 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
355 | return MV88E6390_PORT10_LANE1; | |
356 | return -ENODEV; | |
357 | case 6: | |
358 | if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
359 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
360 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
361 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
362 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
363 | return MV88E6390_PORT10_LANE2; | |
364 | return -ENODEV; | |
365 | case 7: | |
366 | if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
367 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
368 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
369 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
370 | if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) | |
371 | return MV88E6390_PORT10_LANE3; | |
372 | return -ENODEV; | |
373 | case 9: | |
374 | if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
375 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
376 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
377 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || | |
378 | cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
379 | return MV88E6390_PORT9_LANE0; | |
380 | return -ENODEV; | |
381 | case 10: | |
382 | if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || | |
383 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
384 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || | |
385 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || | |
386 | cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) | |
387 | return MV88E6390_PORT10_LANE0; | |
388 | return -ENODEV; | |
389 | default: | |
390 | return -ENODEV; | |
391 | } | |
392 | } | |
393 | ||
6335e9f2 | 394 | /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ |
23ef57d8 AL |
395 | static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, |
396 | bool on) | |
6335e9f2 AL |
397 | { |
398 | u16 val, new_val; | |
6335e9f2 AL |
399 | int err; |
400 | ||
e6891c76 AL |
401 | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, |
402 | MV88E6390_PCS_CONTROL_1, &val); | |
403 | ||
6335e9f2 AL |
404 | if (err) |
405 | return err; | |
406 | ||
407 | if (on) | |
408 | new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | | |
409 | MV88E6390_PCS_CONTROL_1_LOOPBACK | | |
410 | MV88E6390_PCS_CONTROL_1_PDOWN); | |
411 | else | |
412 | new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; | |
413 | ||
414 | if (val != new_val) | |
e6891c76 AL |
415 | err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, |
416 | MV88E6390_PCS_CONTROL_1, new_val); | |
6335e9f2 AL |
417 | |
418 | return err; | |
419 | } | |
420 | ||
a8c01c0d | 421 | /* Set the power on/off for SGMII and 1000Base-X */ |
23ef57d8 AL |
422 | static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, |
423 | bool on) | |
6335e9f2 AL |
424 | { |
425 | u16 val, new_val; | |
6335e9f2 AL |
426 | int err; |
427 | ||
e6891c76 AL |
428 | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, |
429 | MV88E6390_SGMII_CONTROL, &val); | |
6335e9f2 AL |
430 | if (err) |
431 | return err; | |
432 | ||
433 | if (on) | |
434 | new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | | |
435 | MV88E6390_SGMII_CONTROL_LOOPBACK | | |
436 | MV88E6390_SGMII_CONTROL_PDOWN); | |
437 | else | |
438 | new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; | |
439 | ||
440 | if (val != new_val) | |
e6891c76 AL |
441 | err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, |
442 | MV88E6390_SGMII_CONTROL, new_val); | |
6335e9f2 AL |
443 | |
444 | return err; | |
445 | } | |
446 | ||
a8c01c0d AL |
447 | static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, |
448 | int lane, bool on) | |
6335e9f2 | 449 | { |
2d2e1dd2 | 450 | u8 cmode = chip->ports[port].cmode; |
6335e9f2 | 451 | |
6335e9f2 | 452 | switch (cmode) { |
5f83dc93 | 453 | case MV88E6XXX_PORT_STS_CMODE_SGMII: |
a8c01c0d | 454 | case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: |
f8236a08 | 455 | case MV88E6XXX_PORT_STS_CMODE_2500BASEX: |
23ef57d8 | 456 | return mv88e6390_serdes_power_sgmii(chip, lane, on); |
5f83dc93 VD |
457 | case MV88E6XXX_PORT_STS_CMODE_XAUI: |
458 | case MV88E6XXX_PORT_STS_CMODE_RXAUI: | |
23ef57d8 | 459 | return mv88e6390_serdes_power_10g(chip, lane, on); |
6335e9f2 AL |
460 | } |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
465 | int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) | |
466 | { | |
a8c01c0d | 467 | int lane; |
6335e9f2 | 468 | |
a8c01c0d AL |
469 | lane = mv88e6390_serdes_get_lane(chip, port); |
470 | if (lane == -ENODEV) | |
471 | return 0; | |
472 | ||
473 | if (lane < 0) | |
474 | return lane; | |
6335e9f2 | 475 | |
07ffbd74 AL |
476 | switch (port) { |
477 | case 9 ... 10: | |
478 | return mv88e6390_serdes_power_lane(chip, port, lane, on); | |
479 | } | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
484 | int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) | |
485 | { | |
486 | int lane; | |
487 | ||
488 | lane = mv88e6390x_serdes_get_lane(chip, port); | |
489 | if (lane == -ENODEV) | |
490 | return 0; | |
491 | ||
492 | if (lane < 0) | |
493 | return lane; | |
494 | ||
6335e9f2 | 495 | switch (port) { |
a8c01c0d AL |
496 | case 2 ... 4: |
497 | case 5 ... 7: | |
498 | case 9 ... 10: | |
499 | return mv88e6390_serdes_power_lane(chip, port, lane, on); | |
6335e9f2 AL |
500 | } |
501 | ||
502 | return 0; | |
503 | } | |
5bafeb6e | 504 | |
efd1ba6a AL |
505 | static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, |
506 | int port, int lane) | |
507 | { | |
508 | struct dsa_switch *ds = chip->ds; | |
72d8b4fd HK |
509 | int duplex = DUPLEX_UNKNOWN; |
510 | int speed = SPEED_UNKNOWN; | |
511 | int link, err; | |
efd1ba6a | 512 | u16 status; |
efd1ba6a | 513 | |
72d8b4fd HK |
514 | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, |
515 | MV88E6390_SGMII_PHY_STATUS, &status); | |
516 | if (err) { | |
517 | dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err); | |
518 | return; | |
519 | } | |
efd1ba6a | 520 | |
72d8b4fd HK |
521 | link = status & MV88E6390_SGMII_PHY_STATUS_LINK ? |
522 | LINK_FORCED_UP : LINK_FORCED_DOWN; | |
523 | ||
524 | if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { | |
525 | duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? | |
526 | DUPLEX_FULL : DUPLEX_HALF; | |
527 | ||
528 | switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { | |
529 | case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: | |
530 | speed = SPEED_1000; | |
531 | break; | |
532 | case MV88E6390_SGMII_PHY_STATUS_SPEED_100: | |
533 | speed = SPEED_100; | |
534 | break; | |
535 | case MV88E6390_SGMII_PHY_STATUS_SPEED_10: | |
536 | speed = SPEED_10; | |
537 | break; | |
538 | default: | |
539 | dev_err(chip->dev, "invalid PHY speed\n"); | |
540 | return; | |
541 | } | |
542 | } | |
efd1ba6a | 543 | |
72d8b4fd HK |
544 | err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, |
545 | PAUSE_OFF, PHY_INTERFACE_MODE_NA); | |
546 | if (err) | |
547 | dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", | |
548 | err); | |
549 | else | |
550 | dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP); | |
efd1ba6a AL |
551 | } |
552 | ||
553 | static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, | |
554 | int lane) | |
555 | { | |
556 | return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, | |
557 | MV88E6390_SGMII_INT_ENABLE, | |
558 | MV88E6390_SGMII_INT_LINK_DOWN | | |
559 | MV88E6390_SGMII_INT_LINK_UP); | |
560 | } | |
561 | ||
562 | static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip, | |
563 | int lane) | |
564 | { | |
565 | return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, | |
566 | MV88E6390_SGMII_INT_ENABLE, 0); | |
567 | } | |
568 | ||
569 | int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, | |
570 | int lane) | |
571 | { | |
572 | u8 cmode = chip->ports[port].cmode; | |
573 | int err = 0; | |
574 | ||
575 | switch (cmode) { | |
576 | case MV88E6XXX_PORT_STS_CMODE_SGMII: | |
577 | case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: | |
578 | case MV88E6XXX_PORT_STS_CMODE_2500BASEX: | |
579 | err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); | |
580 | } | |
581 | ||
582 | return err; | |
583 | } | |
584 | ||
585 | int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, | |
586 | int lane) | |
587 | { | |
588 | u8 cmode = chip->ports[port].cmode; | |
589 | int err = 0; | |
590 | ||
591 | switch (cmode) { | |
592 | case MV88E6XXX_PORT_STS_CMODE_SGMII: | |
593 | case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: | |
594 | case MV88E6XXX_PORT_STS_CMODE_2500BASEX: | |
595 | err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); | |
596 | } | |
597 | ||
598 | return err; | |
599 | } | |
600 | ||
601 | static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, | |
602 | int lane, u16 *status) | |
603 | { | |
604 | int err; | |
605 | ||
606 | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, | |
607 | MV88E6390_SGMII_INT_STATUS, status); | |
608 | ||
609 | return err; | |
610 | } | |
611 | ||
612 | static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id) | |
613 | { | |
614 | struct mv88e6xxx_port *port = dev_id; | |
615 | struct mv88e6xxx_chip *chip = port->chip; | |
616 | irqreturn_t ret = IRQ_NONE; | |
617 | u8 cmode = port->cmode; | |
618 | u16 status; | |
619 | int lane; | |
620 | int err; | |
621 | ||
622 | lane = mv88e6390x_serdes_get_lane(chip, port->port); | |
623 | ||
624 | mutex_lock(&chip->reg_lock); | |
625 | ||
626 | switch (cmode) { | |
627 | case MV88E6XXX_PORT_STS_CMODE_SGMII: | |
628 | case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: | |
629 | case MV88E6XXX_PORT_STS_CMODE_2500BASEX: | |
630 | err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); | |
631 | if (err) | |
632 | goto out; | |
6feddb49 DC |
633 | if (status & (MV88E6390_SGMII_INT_LINK_DOWN | |
634 | MV88E6390_SGMII_INT_LINK_UP)) { | |
efd1ba6a AL |
635 | ret = IRQ_HANDLED; |
636 | mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane); | |
637 | } | |
638 | } | |
639 | out: | |
640 | mutex_unlock(&chip->reg_lock); | |
641 | ||
642 | return ret; | |
643 | } | |
644 | ||
2defda1f | 645 | int mv88e6390x_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) |
efd1ba6a AL |
646 | { |
647 | int lane; | |
648 | int err; | |
649 | ||
efd1ba6a AL |
650 | lane = mv88e6390x_serdes_get_lane(chip, port); |
651 | ||
652 | if (lane == -ENODEV) | |
653 | return 0; | |
654 | ||
655 | if (lane < 0) | |
656 | return lane; | |
657 | ||
658 | chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, | |
659 | port); | |
660 | if (chip->ports[port].serdes_irq < 0) { | |
661 | dev_err(chip->dev, "Unable to map SERDES irq: %d\n", | |
662 | chip->ports[port].serdes_irq); | |
663 | return chip->ports[port].serdes_irq; | |
664 | } | |
665 | ||
666 | /* Requesting the IRQ will trigger irq callbacks. So we cannot | |
667 | * hold the reg_lock. | |
668 | */ | |
669 | mutex_unlock(&chip->reg_lock); | |
670 | err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, | |
671 | mv88e6390_serdes_thread_fn, | |
672 | IRQF_ONESHOT, "mv88e6xxx-serdes", | |
673 | &chip->ports[port]); | |
674 | mutex_lock(&chip->reg_lock); | |
675 | ||
676 | if (err) { | |
677 | dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", | |
678 | err); | |
679 | return err; | |
680 | } | |
681 | ||
682 | return mv88e6390_serdes_irq_enable(chip, port, lane); | |
683 | } | |
684 | ||
2defda1f AL |
685 | int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) |
686 | { | |
687 | if (port < 9) | |
688 | return 0; | |
689 | ||
6fb6e637 | 690 | return mv88e6390x_serdes_irq_setup(chip, port); |
2defda1f AL |
691 | } |
692 | ||
693 | void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) | |
efd1ba6a AL |
694 | { |
695 | int lane = mv88e6390x_serdes_get_lane(chip, port); | |
696 | ||
2defda1f | 697 | if (lane == -ENODEV) |
efd1ba6a AL |
698 | return; |
699 | ||
700 | if (lane < 0) | |
701 | return; | |
702 | ||
703 | mv88e6390_serdes_irq_disable(chip, port, lane); | |
704 | ||
705 | /* Freeing the IRQ will trigger irq callbacks. So we cannot | |
706 | * hold the reg_lock. | |
707 | */ | |
708 | mutex_unlock(&chip->reg_lock); | |
709 | free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); | |
710 | mutex_lock(&chip->reg_lock); | |
734447d4 AL |
711 | |
712 | chip->ports[port].serdes_irq = 0; | |
efd1ba6a AL |
713 | } |
714 | ||
2defda1f AL |
715 | void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) |
716 | { | |
717 | if (port < 9) | |
718 | return; | |
719 | ||
720 | mv88e6390x_serdes_irq_free(chip, port); | |
721 | } | |
722 | ||
5bafeb6e MB |
723 | int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) |
724 | { | |
2d2e1dd2 | 725 | u8 cmode = chip->ports[port].cmode; |
5bafeb6e MB |
726 | |
727 | if (port != 5) | |
728 | return 0; | |
729 | ||
5bafeb6e MB |
730 | if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || |
731 | cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || | |
732 | cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) | |
23ef57d8 AL |
733 | return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES, |
734 | on); | |
5bafeb6e MB |
735 | |
736 | return 0; | |
737 | } |