net: dsa: mv88e6xxx: Cache the port cmode
[linux-2.6-block.git] / drivers / net / dsa / mv88e6xxx / serdes.c
CommitLineData
6d91782f
AL
1/*
2 * Marvell 88E6xxx SERDES manipulation, via SMI bus
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/mii.h>
15
4d5f2ba7 16#include "chip.h"
6335e9f2 17#include "global2.h"
6d91782f
AL
18#include "phy.h"
19#include "port.h"
20#include "serdes.h"
21
22static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
23 u16 *val)
24{
25 return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
26 MV88E6352_SERDES_PAGE_FIBER,
27 reg, val);
28}
29
30static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
31 u16 val)
32{
33 return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
34 MV88E6352_SERDES_PAGE_FIBER,
35 reg, val);
36}
37
e6891c76
AL
38static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
39 int lane, int device, int reg, u16 *val)
40{
41 int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
42
43 return mv88e6xxx_phy_read(chip, lane, reg_c45, val);
44}
45
46static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
47 int lane, int device, int reg, u16 val)
48{
49 int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
50
51 return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
52}
53
6d91782f
AL
54static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
55{
56 u16 val, new_val;
57 int err;
58
59 err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
60 if (err)
61 return err;
62
63 if (on)
64 new_val = val & ~BMCR_PDOWN;
65 else
66 new_val = val | BMCR_PDOWN;
67
68 if (val != new_val)
69 err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
70
71 return err;
72}
73
eb755c3f 74static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
6d91782f 75{
2d2e1dd2 76 u8 cmode = chip->ports[port].cmode;
6d91782f 77
5f83dc93
VD
78 if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
79 (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
eb755c3f 80 (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
b1312b85 81 return true;
eb755c3f 82
b1312b85 83 return false;
eb755c3f
AL
84}
85
86int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
87{
88 int err;
89
90 if (mv88e6352_port_has_serdes(chip, port)) {
6d91782f
AL
91 err = mv88e6352_serdes_power_set(chip, on);
92 if (err < 0)
93 return err;
94 }
95
96 return 0;
97}
6335e9f2 98
cda9f4aa
AL
99struct mv88e6352_serdes_hw_stat {
100 char string[ETH_GSTRING_LEN];
101 int sizeof_stat;
102 int reg;
103};
104
105static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
106 { "serdes_fibre_rx_error", 16, 21 },
107 { "serdes_PRBS_error", 32, 24 },
108};
109
110int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
111{
112 if (mv88e6352_port_has_serdes(chip, port))
113 return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
114
115 return 0;
116}
117
65f60e45
AL
118int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
119 int port, uint8_t *data)
cda9f4aa
AL
120{
121 struct mv88e6352_serdes_hw_stat *stat;
122 int i;
123
124 if (!mv88e6352_port_has_serdes(chip, port))
65f60e45 125 return 0;
cda9f4aa
AL
126
127 for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
128 stat = &mv88e6352_serdes_hw_stats[i];
129 memcpy(data + i * ETH_GSTRING_LEN, stat->string,
130 ETH_GSTRING_LEN);
131 }
65f60e45 132 return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
cda9f4aa
AL
133}
134
135static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
136 struct mv88e6352_serdes_hw_stat *stat)
137{
138 u64 val = 0;
139 u16 reg;
140 int err;
141
142 err = mv88e6352_serdes_read(chip, stat->reg, &reg);
143 if (err) {
144 dev_err(chip->dev, "failed to read statistic\n");
145 return 0;
146 }
147
148 val = reg;
149
150 if (stat->sizeof_stat == 32) {
151 err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
152 if (err) {
153 dev_err(chip->dev, "failed to read statistic\n");
154 return 0;
155 }
156 val = val << 16 | reg;
157 }
158
159 return val;
160}
161
65f60e45
AL
162int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
163 uint64_t *data)
cda9f4aa
AL
164{
165 struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
166 struct mv88e6352_serdes_hw_stat *stat;
167 u64 value;
168 int i;
169
170 if (!mv88e6352_port_has_serdes(chip, port))
65f60e45 171 return 0;
cda9f4aa
AL
172
173 BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
174 ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
175
176 for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
177 stat = &mv88e6352_serdes_hw_stats[i];
178 value = mv88e6352_serdes_get_stat(chip, stat);
179 mv88e6xxx_port->serdes_stats[i] += value;
180 data[i] = mv88e6xxx_port->serdes_stats[i];
181 }
65f60e45
AL
182
183 return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
cda9f4aa
AL
184}
185
07ffbd74
AL
186/* Return the SERDES lane address a port is using. Only Ports 9 and 10
187 * have SERDES lanes. Returns -ENODEV if a port does not have a lane.
188 */
189static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
190{
2d2e1dd2 191 u8 cmode = chip->ports[port].cmode;
07ffbd74
AL
192
193 switch (port) {
194 case 9:
195 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
196 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
197 cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
198 return MV88E6390_PORT9_LANE0;
199 return -ENODEV;
200 case 10:
201 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
202 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
203 cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
204 return MV88E6390_PORT10_LANE0;
205 return -ENODEV;
206 default:
207 return -ENODEV;
208 }
209}
210
a8c01c0d
AL
211/* Return the SERDES lane address a port is using. Ports 9 and 10 can
212 * use multiple lanes. If so, return the first lane the port uses.
213 * Returns -ENODEV if a port does not have a lane.
214 */
07ffbd74 215static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
a8c01c0d
AL
216{
217 u8 cmode_port9, cmode_port10, cmode_port;
a8c01c0d 218
2d2e1dd2
AL
219 cmode_port9 = chip->ports[9].cmode;
220 cmode_port10 = chip->ports[10].cmode;
221 cmode_port = chip->ports[port].cmode;
a8c01c0d
AL
222
223 switch (port) {
224 case 2:
225 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
226 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
227 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
228 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
229 return MV88E6390_PORT9_LANE1;
230 return -ENODEV;
231 case 3:
232 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
233 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
234 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
235 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
236 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
237 return MV88E6390_PORT9_LANE2;
238 return -ENODEV;
239 case 4:
240 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
241 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
242 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
243 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
244 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
245 return MV88E6390_PORT9_LANE3;
246 return -ENODEV;
247 case 5:
248 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
249 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
250 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
251 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
252 return MV88E6390_PORT10_LANE1;
253 return -ENODEV;
254 case 6:
255 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
256 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
257 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
258 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
259 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
260 return MV88E6390_PORT10_LANE2;
261 return -ENODEV;
262 case 7:
263 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
264 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
265 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
266 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
267 if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
268 return MV88E6390_PORT10_LANE3;
269 return -ENODEV;
270 case 9:
271 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
272 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
273 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
274 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
275 cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
276 return MV88E6390_PORT9_LANE0;
277 return -ENODEV;
278 case 10:
279 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
280 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
281 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
282 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
283 cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
284 return MV88E6390_PORT10_LANE0;
285 return -ENODEV;
286 default:
287 return -ENODEV;
288 }
289}
290
6335e9f2 291/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
23ef57d8
AL
292static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
293 bool on)
6335e9f2
AL
294{
295 u16 val, new_val;
6335e9f2
AL
296 int err;
297
e6891c76
AL
298 err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
299 MV88E6390_PCS_CONTROL_1, &val);
300
6335e9f2
AL
301 if (err)
302 return err;
303
304 if (on)
305 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
306 MV88E6390_PCS_CONTROL_1_LOOPBACK |
307 MV88E6390_PCS_CONTROL_1_PDOWN);
308 else
309 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
310
311 if (val != new_val)
e6891c76
AL
312 err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
313 MV88E6390_PCS_CONTROL_1, new_val);
6335e9f2
AL
314
315 return err;
316}
317
a8c01c0d 318/* Set the power on/off for SGMII and 1000Base-X */
23ef57d8
AL
319static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
320 bool on)
6335e9f2
AL
321{
322 u16 val, new_val;
6335e9f2
AL
323 int err;
324
e6891c76
AL
325 err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
326 MV88E6390_SGMII_CONTROL, &val);
6335e9f2
AL
327 if (err)
328 return err;
329
330 if (on)
331 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
332 MV88E6390_SGMII_CONTROL_LOOPBACK |
333 MV88E6390_SGMII_CONTROL_PDOWN);
334 else
335 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
336
337 if (val != new_val)
e6891c76
AL
338 err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
339 MV88E6390_SGMII_CONTROL, new_val);
6335e9f2
AL
340
341 return err;
342}
343
a8c01c0d
AL
344static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port,
345 int lane, bool on)
6335e9f2 346{
2d2e1dd2 347 u8 cmode = chip->ports[port].cmode;
6335e9f2 348
6335e9f2 349 switch (cmode) {
5f83dc93 350 case MV88E6XXX_PORT_STS_CMODE_SGMII:
a8c01c0d 351 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
f8236a08 352 case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
23ef57d8 353 return mv88e6390_serdes_power_sgmii(chip, lane, on);
5f83dc93
VD
354 case MV88E6XXX_PORT_STS_CMODE_XAUI:
355 case MV88E6XXX_PORT_STS_CMODE_RXAUI:
23ef57d8 356 return mv88e6390_serdes_power_10g(chip, lane, on);
6335e9f2
AL
357 }
358
359 return 0;
360}
361
362int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
363{
a8c01c0d 364 int lane;
6335e9f2 365
a8c01c0d
AL
366 lane = mv88e6390_serdes_get_lane(chip, port);
367 if (lane == -ENODEV)
368 return 0;
369
370 if (lane < 0)
371 return lane;
6335e9f2 372
07ffbd74
AL
373 switch (port) {
374 case 9 ... 10:
375 return mv88e6390_serdes_power_lane(chip, port, lane, on);
376 }
377
378 return 0;
379}
380
381int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
382{
383 int lane;
384
385 lane = mv88e6390x_serdes_get_lane(chip, port);
386 if (lane == -ENODEV)
387 return 0;
388
389 if (lane < 0)
390 return lane;
391
6335e9f2 392 switch (port) {
a8c01c0d
AL
393 case 2 ... 4:
394 case 5 ... 7:
395 case 9 ... 10:
396 return mv88e6390_serdes_power_lane(chip, port, lane, on);
6335e9f2
AL
397 }
398
399 return 0;
400}
5bafeb6e
MB
401
402int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
403{
2d2e1dd2 404 u8 cmode = chip->ports[port].cmode;
5bafeb6e
MB
405
406 if (port != 5)
407 return 0;
408
5bafeb6e
MB
409 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
410 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
411 cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
23ef57d8
AL
412 return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES,
413 on);
5bafeb6e
MB
414
415 return 0;
416}