xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision 4da722ca)
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 }
230