GNU Linux-libre 4.14.254-gnu1
[releases.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 mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
39 {
40         u16 val, new_val;
41         int err;
42
43         err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
44         if (err)
45                 return err;
46
47         if (on)
48                 new_val = val & ~BMCR_PDOWN;
49         else
50                 new_val = val | BMCR_PDOWN;
51
52         if (val != new_val)
53                 err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
54
55         return err;
56 }
57
58 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
59 {
60         int err;
61         u8 cmode;
62
63         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
64         if (err)
65                 return err;
66
67         if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
68             (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
69             (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) {
70                 err = mv88e6352_serdes_power_set(chip, on);
71                 if (err < 0)
72                         return err;
73         }
74
75         return 0;
76 }
77
78 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
79 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
80 {
81         u16 val, new_val;
82         int reg_c45;
83         int err;
84
85         reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
86                 MV88E6390_PCS_CONTROL_1;
87         err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
88         if (err)
89                 return err;
90
91         if (on)
92                 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
93                                   MV88E6390_PCS_CONTROL_1_LOOPBACK |
94                                   MV88E6390_PCS_CONTROL_1_PDOWN);
95         else
96                 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
97
98         if (val != new_val)
99                 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
100
101         return err;
102 }
103
104 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
105 static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr,
106                                   bool on)
107 {
108         u16 val, new_val;
109         int reg_c45;
110         int err;
111
112         reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
113                 MV88E6390_SGMII_CONTROL;
114         err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
115         if (err)
116                 return err;
117
118         if (on)
119                 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
120                                   MV88E6390_SGMII_CONTROL_LOOPBACK |
121                                   MV88E6390_SGMII_CONTROL_PDOWN);
122         else
123                 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
124
125         if (val != new_val)
126                 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
127
128         return err;
129 }
130
131 static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode,
132                                   int port_donor, int lane, bool rxaui, bool on)
133 {
134         int err;
135         u8 cmode_donor;
136
137         err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor);
138         if (err)
139                 return err;
140
141         switch (cmode_donor) {
142         case MV88E6XXX_PORT_STS_CMODE_RXAUI:
143                 if (!rxaui)
144                         break;
145                 /* Fall through */
146         case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
147         case MV88E6XXX_PORT_STS_CMODE_SGMII:
148         case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
149                 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
150                     cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)
151                         return  mv88e6390_serdes_sgmii(chip, lane, on);
152         }
153         return 0;
154 }
155
156 static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode,
157                                   bool on)
158 {
159         switch (cmode) {
160         case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
161         case MV88E6XXX_PORT_STS_CMODE_SGMII:
162                 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on);
163         case MV88E6XXX_PORT_STS_CMODE_XAUI:
164         case MV88E6XXX_PORT_STS_CMODE_RXAUI:
165         case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
166                 return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on);
167         }
168
169         return 0;
170 }
171
172 static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode,
173                                    bool on)
174 {
175         switch (cmode) {
176         case MV88E6XXX_PORT_STS_CMODE_SGMII:
177                 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on);
178         case MV88E6XXX_PORT_STS_CMODE_XAUI:
179         case MV88E6XXX_PORT_STS_CMODE_RXAUI:
180         case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
181         case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
182                 return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on);
183         }
184
185         return 0;
186 }
187
188 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
189 {
190         u8 cmode;
191         int err;
192
193         err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
194         if (err)
195                 return err;
196
197         switch (port) {
198         case 2:
199                 return mv88e6390_serdes_lower(chip, cmode, 9,
200                                               MV88E6390_PORT9_LANE1,
201                                               false, on);
202         case 3:
203                 return mv88e6390_serdes_lower(chip, cmode, 9,
204                                               MV88E6390_PORT9_LANE2,
205                                               true, on);
206         case 4:
207                 return mv88e6390_serdes_lower(chip, cmode, 9,
208                                               MV88E6390_PORT9_LANE3,
209                                               true, on);
210         case 5:
211                 return mv88e6390_serdes_lower(chip, cmode, 10,
212                                               MV88E6390_PORT10_LANE1,
213                                               false, on);
214         case 6:
215                 return mv88e6390_serdes_lower(chip, cmode, 10,
216                                               MV88E6390_PORT10_LANE2,
217                                               true, on);
218         case 7:
219                 return mv88e6390_serdes_lower(chip, cmode, 10,
220                                               MV88E6390_PORT10_LANE3,
221                                               true, on);
222         case 9:
223                 return mv88e6390_serdes_port9(chip, cmode, on);
224         case 10:
225                 return mv88e6390_serdes_port10(chip, cmode, on);
226         }
227
228         return 0;
229 }