net: dsa: mv88e6xxx: Add serdes register read/write helper
[linux-2.6-block.git] / drivers / net / dsa / mv88e6xxx / serdes.c
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
16 #include "chip.h"
17 #include "global2.h"
18 #include "phy.h"
19 #include "port.h"
20 #include "serdes.h"
21
22 static 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
30 static 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
38 static 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
46 static 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
54 static 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
74 static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
75 {
76         u8 cmode;
77         int err;
78
79         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
80         if (err) {
81                 dev_err(chip->dev, "failed to read cmode\n");
82                 return false;
83         }
84
85         if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
86             (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
87             (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
88                 return true;
89
90         return false;
91 }
92
93 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
94 {
95         int err;
96
97         if (mv88e6352_port_has_serdes(chip, port)) {
98                 err = mv88e6352_serdes_power_set(chip, on);
99                 if (err < 0)
100                         return err;
101         }
102
103         return 0;
104 }
105
106 struct mv88e6352_serdes_hw_stat {
107         char string[ETH_GSTRING_LEN];
108         int sizeof_stat;
109         int reg;
110 };
111
112 static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
113         { "serdes_fibre_rx_error", 16, 21 },
114         { "serdes_PRBS_error", 32, 24 },
115 };
116
117 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
118 {
119         if (mv88e6352_port_has_serdes(chip, port))
120                 return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
121
122         return 0;
123 }
124
125 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
126                                  int port, uint8_t *data)
127 {
128         struct mv88e6352_serdes_hw_stat *stat;
129         int i;
130
131         if (!mv88e6352_port_has_serdes(chip, port))
132                 return 0;
133
134         for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
135                 stat = &mv88e6352_serdes_hw_stats[i];
136                 memcpy(data + i * ETH_GSTRING_LEN, stat->string,
137                        ETH_GSTRING_LEN);
138         }
139         return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
140 }
141
142 static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
143                                           struct mv88e6352_serdes_hw_stat *stat)
144 {
145         u64 val = 0;
146         u16 reg;
147         int err;
148
149         err = mv88e6352_serdes_read(chip, stat->reg, &reg);
150         if (err) {
151                 dev_err(chip->dev, "failed to read statistic\n");
152                 return 0;
153         }
154
155         val = reg;
156
157         if (stat->sizeof_stat == 32) {
158                 err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
159                 if (err) {
160                         dev_err(chip->dev, "failed to read statistic\n");
161                         return 0;
162                 }
163                 val = val << 16 | reg;
164         }
165
166         return val;
167 }
168
169 int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
170                                uint64_t *data)
171 {
172         struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
173         struct mv88e6352_serdes_hw_stat *stat;
174         u64 value;
175         int i;
176
177         if (!mv88e6352_port_has_serdes(chip, port))
178                 return 0;
179
180         BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
181                      ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
182
183         for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
184                 stat = &mv88e6352_serdes_hw_stats[i];
185                 value = mv88e6352_serdes_get_stat(chip, stat);
186                 mv88e6xxx_port->serdes_stats[i] += value;
187                 data[i] = mv88e6xxx_port->serdes_stats[i];
188         }
189
190         return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
191 }
192
193 /* Return the SERDES lane address a port is using. Only Ports 9 and 10
194  * have SERDES lanes. Returns -ENODEV if a port does not have a lane.
195  */
196 static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
197 {
198         u8 cmode;
199         int err;
200
201         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
202         if (err)
203                 return err;
204
205         switch (port) {
206         case 9:
207                 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
208                     cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
209                     cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
210                         return MV88E6390_PORT9_LANE0;
211                 return -ENODEV;
212         case 10:
213                 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
214                     cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
215                     cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
216                         return MV88E6390_PORT10_LANE0;
217                 return -ENODEV;
218         default:
219                 return -ENODEV;
220         }
221 }
222
223 /* Return the SERDES lane address a port is using. Ports 9 and 10 can
224  * use multiple lanes. If so, return the first lane the port uses.
225  * Returns -ENODEV if a port does not have a lane.
226  */
227 static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
228 {
229         u8 cmode_port9, cmode_port10, cmode_port;
230         int err;
231
232         err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9);
233         if (err)
234                 return err;
235
236         err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10);
237         if (err)
238                 return err;
239
240         err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port);
241         if (err)
242                 return err;
243
244         switch (port) {
245         case 2:
246                 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
247                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
248                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
249                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
250                                 return MV88E6390_PORT9_LANE1;
251                 return -ENODEV;
252         case 3:
253                 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
254                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
255                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
256                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
257                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
258                                 return MV88E6390_PORT9_LANE2;
259                 return -ENODEV;
260         case 4:
261                 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
262                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
263                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
264                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
265                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
266                                 return MV88E6390_PORT9_LANE3;
267                 return -ENODEV;
268         case 5:
269                 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
270                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
271                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
272                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
273                                 return MV88E6390_PORT10_LANE1;
274                 return -ENODEV;
275         case 6:
276                 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
277                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
278                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
279                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
280                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
281                                 return MV88E6390_PORT10_LANE2;
282                 return -ENODEV;
283         case 7:
284                 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
285                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
286                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
287                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
288                         if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
289                                 return MV88E6390_PORT10_LANE3;
290                 return -ENODEV;
291         case 9:
292                 if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
293                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
294                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
295                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
296                     cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
297                         return MV88E6390_PORT9_LANE0;
298                 return -ENODEV;
299         case 10:
300                 if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
301                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
302                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
303                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
304                     cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
305                         return MV88E6390_PORT10_LANE0;
306                 return -ENODEV;
307         default:
308                 return -ENODEV;
309         }
310 }
311
312 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
313 static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
314                                       bool on)
315 {
316         u16 val, new_val;
317         int err;
318
319         err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
320                                     MV88E6390_PCS_CONTROL_1, &val);
321
322         if (err)
323                 return err;
324
325         if (on)
326                 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
327                                   MV88E6390_PCS_CONTROL_1_LOOPBACK |
328                                   MV88E6390_PCS_CONTROL_1_PDOWN);
329         else
330                 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
331
332         if (val != new_val)
333                 err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
334                                              MV88E6390_PCS_CONTROL_1, new_val);
335
336         return err;
337 }
338
339 /* Set the power on/off for SGMII and 1000Base-X */
340 static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
341                                         bool on)
342 {
343         u16 val, new_val;
344         int err;
345
346         err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
347                                     MV88E6390_SGMII_CONTROL, &val);
348         if (err)
349                 return err;
350
351         if (on)
352                 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
353                                   MV88E6390_SGMII_CONTROL_LOOPBACK |
354                                   MV88E6390_SGMII_CONTROL_PDOWN);
355         else
356                 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
357
358         if (val != new_val)
359                 err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
360                                              MV88E6390_SGMII_CONTROL, new_val);
361
362         return err;
363 }
364
365 static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port,
366                                        int lane, bool on)
367 {
368         u8 cmode;
369         int err;
370
371         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
372         if (err)
373                 return err;
374
375         switch (cmode) {
376         case MV88E6XXX_PORT_STS_CMODE_SGMII:
377         case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
378                 return mv88e6390_serdes_power_sgmii(chip, lane, on);
379         case MV88E6XXX_PORT_STS_CMODE_XAUI:
380         case MV88E6XXX_PORT_STS_CMODE_RXAUI:
381         case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
382                 return mv88e6390_serdes_power_10g(chip, lane, on);
383         }
384
385         return 0;
386 }
387
388 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
389 {
390         int lane;
391
392         lane = mv88e6390_serdes_get_lane(chip, port);
393         if (lane == -ENODEV)
394                 return 0;
395
396         if (lane < 0)
397                 return lane;
398
399         switch (port) {
400         case 9 ... 10:
401                 return mv88e6390_serdes_power_lane(chip, port, lane, on);
402         }
403
404         return 0;
405 }
406
407 int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
408 {
409         int lane;
410
411         lane = mv88e6390x_serdes_get_lane(chip, port);
412         if (lane == -ENODEV)
413                 return 0;
414
415         if (lane < 0)
416                 return lane;
417
418         switch (port) {
419         case 2 ... 4:
420         case 5 ... 7:
421         case 9 ... 10:
422                 return mv88e6390_serdes_power_lane(chip, port, lane, on);
423         }
424
425         return 0;
426 }
427
428 int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
429 {
430         int err;
431         u8 cmode;
432
433         if (port != 5)
434                 return 0;
435
436         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
437         if (err)
438                 return err;
439
440         if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
441             cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
442             cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
443                 return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES,
444                                                     on);
445
446         return 0;
447 }