2 * Marvell 88E6xxx SERDES manipulation, via SMI bus
4 * Copyright (c) 2008 Marvell Semiconductor
6 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
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.
14 #include <linux/mii.h>
22 static int mv88e6352_serdes_read(struct mv88e6xxx_chip
*chip
, int reg
,
25 return mv88e6xxx_phy_page_read(chip
, MV88E6352_ADDR_SERDES
,
26 MV88E6352_SERDES_PAGE_FIBER
,
30 static int mv88e6352_serdes_write(struct mv88e6xxx_chip
*chip
, int reg
,
33 return mv88e6xxx_phy_page_write(chip
, MV88E6352_ADDR_SERDES
,
34 MV88E6352_SERDES_PAGE_FIBER
,
38 static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip
*chip
, bool on
)
43 err
= mv88e6352_serdes_read(chip
, MII_BMCR
, &val
);
48 new_val
= val
& ~BMCR_PDOWN
;
50 new_val
= val
| BMCR_PDOWN
;
53 err
= mv88e6352_serdes_write(chip
, MII_BMCR
, new_val
);
58 static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip
*chip
, int port
)
63 err
= mv88e6xxx_port_get_cmode(chip
, port
, &cmode
);
65 dev_err(chip
->dev
, "failed to read cmode\n");
69 if ((cmode
== MV88E6XXX_PORT_STS_CMODE_100BASE_X
) ||
70 (cmode
== MV88E6XXX_PORT_STS_CMODE_1000BASE_X
) ||
71 (cmode
== MV88E6XXX_PORT_STS_CMODE_SGMII
))
77 int mv88e6352_serdes_power(struct mv88e6xxx_chip
*chip
, int port
, bool on
)
81 if (mv88e6352_port_has_serdes(chip
, port
)) {
82 err
= mv88e6352_serdes_power_set(chip
, on
);
90 struct mv88e6352_serdes_hw_stat
{
91 char string
[ETH_GSTRING_LEN
];
96 static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats
[] = {
97 { "serdes_fibre_rx_error", 16, 21 },
98 { "serdes_PRBS_error", 32, 24 },
101 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip
*chip
, int port
)
103 if (mv88e6352_port_has_serdes(chip
, port
))
104 return ARRAY_SIZE(mv88e6352_serdes_hw_stats
);
109 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip
*chip
,
110 int port
, uint8_t *data
)
112 struct mv88e6352_serdes_hw_stat
*stat
;
115 if (!mv88e6352_port_has_serdes(chip
, port
))
118 for (i
= 0; i
< ARRAY_SIZE(mv88e6352_serdes_hw_stats
); i
++) {
119 stat
= &mv88e6352_serdes_hw_stats
[i
];
120 memcpy(data
+ i
* ETH_GSTRING_LEN
, stat
->string
,
123 return ARRAY_SIZE(mv88e6352_serdes_hw_stats
);
126 static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip
*chip
,
127 struct mv88e6352_serdes_hw_stat
*stat
)
133 err
= mv88e6352_serdes_read(chip
, stat
->reg
, ®
);
135 dev_err(chip
->dev
, "failed to read statistic\n");
141 if (stat
->sizeof_stat
== 32) {
142 err
= mv88e6352_serdes_read(chip
, stat
->reg
+ 1, ®
);
144 dev_err(chip
->dev
, "failed to read statistic\n");
147 val
= val
<< 16 | reg
;
153 int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip
*chip
, int port
,
156 struct mv88e6xxx_port
*mv88e6xxx_port
= &chip
->ports
[port
];
157 struct mv88e6352_serdes_hw_stat
*stat
;
161 if (!mv88e6352_port_has_serdes(chip
, port
))
164 BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats
) >
165 ARRAY_SIZE(mv88e6xxx_port
->serdes_stats
));
167 for (i
= 0; i
< ARRAY_SIZE(mv88e6352_serdes_hw_stats
); i
++) {
168 stat
= &mv88e6352_serdes_hw_stats
[i
];
169 value
= mv88e6352_serdes_get_stat(chip
, stat
);
170 mv88e6xxx_port
->serdes_stats
[i
] += value
;
171 data
[i
] = mv88e6xxx_port
->serdes_stats
[i
];
174 return ARRAY_SIZE(mv88e6352_serdes_hw_stats
);
177 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
178 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip
*chip
, int addr
, bool on
)
184 reg_c45
= MII_ADDR_C45
| MV88E6390_SERDES_DEVICE
|
185 MV88E6390_PCS_CONTROL_1
;
186 err
= mv88e6xxx_phy_read(chip
, addr
, reg_c45
, &val
);
191 new_val
= val
& ~(MV88E6390_PCS_CONTROL_1_RESET
|
192 MV88E6390_PCS_CONTROL_1_LOOPBACK
|
193 MV88E6390_PCS_CONTROL_1_PDOWN
);
195 new_val
= val
| MV88E6390_PCS_CONTROL_1_PDOWN
;
198 err
= mv88e6xxx_phy_write(chip
, addr
, reg_c45
, new_val
);
203 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
204 static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip
*chip
, int addr
,
211 reg_c45
= MII_ADDR_C45
| MV88E6390_SERDES_DEVICE
|
212 MV88E6390_SGMII_CONTROL
;
213 err
= mv88e6xxx_phy_read(chip
, addr
, reg_c45
, &val
);
218 new_val
= val
& ~(MV88E6390_SGMII_CONTROL_RESET
|
219 MV88E6390_SGMII_CONTROL_LOOPBACK
|
220 MV88E6390_SGMII_CONTROL_PDOWN
);
222 new_val
= val
| MV88E6390_SGMII_CONTROL_PDOWN
;
225 err
= mv88e6xxx_phy_write(chip
, addr
, reg_c45
, new_val
);
230 static int mv88e6390_serdes_lower(struct mv88e6xxx_chip
*chip
, u8 cmode
,
231 int port_donor
, int lane
, bool rxaui
, bool on
)
236 err
= mv88e6xxx_port_get_cmode(chip
, port_donor
, &cmode_donor
);
240 switch (cmode_donor
) {
241 case MV88E6XXX_PORT_STS_CMODE_RXAUI
:
245 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X
:
246 case MV88E6XXX_PORT_STS_CMODE_SGMII
:
247 case MV88E6XXX_PORT_STS_CMODE_2500BASEX
:
248 if (cmode
== MV88E6XXX_PORT_STS_CMODE_1000BASE_X
||
249 cmode
== MV88E6XXX_PORT_STS_CMODE_SGMII
)
250 return mv88e6390_serdes_sgmii(chip
, lane
, on
);
255 static int mv88e6390_serdes_port9(struct mv88e6xxx_chip
*chip
, u8 cmode
,
259 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X
:
260 case MV88E6XXX_PORT_STS_CMODE_SGMII
:
261 return mv88e6390_serdes_sgmii(chip
, MV88E6390_PORT9_LANE0
, on
);
262 case MV88E6XXX_PORT_STS_CMODE_XAUI
:
263 case MV88E6XXX_PORT_STS_CMODE_RXAUI
:
264 case MV88E6XXX_PORT_STS_CMODE_2500BASEX
:
265 return mv88e6390_serdes_10g(chip
, MV88E6390_PORT9_LANE0
, on
);
271 static int mv88e6390_serdes_port10(struct mv88e6xxx_chip
*chip
, u8 cmode
,
275 case MV88E6XXX_PORT_STS_CMODE_SGMII
:
276 return mv88e6390_serdes_sgmii(chip
, MV88E6390_PORT10_LANE0
, on
);
277 case MV88E6XXX_PORT_STS_CMODE_XAUI
:
278 case MV88E6XXX_PORT_STS_CMODE_RXAUI
:
279 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X
:
280 case MV88E6XXX_PORT_STS_CMODE_2500BASEX
:
281 return mv88e6390_serdes_10g(chip
, MV88E6390_PORT10_LANE0
, on
);
287 int mv88e6390_serdes_power(struct mv88e6xxx_chip
*chip
, int port
, bool on
)
292 err
= mv88e6xxx_port_get_cmode(chip
, port
, &cmode
);
298 return mv88e6390_serdes_lower(chip
, cmode
, 9,
299 MV88E6390_PORT9_LANE1
,
302 return mv88e6390_serdes_lower(chip
, cmode
, 9,
303 MV88E6390_PORT9_LANE2
,
306 return mv88e6390_serdes_lower(chip
, cmode
, 9,
307 MV88E6390_PORT9_LANE3
,
310 return mv88e6390_serdes_lower(chip
, cmode
, 10,
311 MV88E6390_PORT10_LANE1
,
314 return mv88e6390_serdes_lower(chip
, cmode
, 10,
315 MV88E6390_PORT10_LANE2
,
318 return mv88e6390_serdes_lower(chip
, cmode
, 10,
319 MV88E6390_PORT10_LANE3
,
322 return mv88e6390_serdes_port9(chip
, cmode
, on
);
324 return mv88e6390_serdes_port10(chip
, cmode
, on
);