Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 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
 38static 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
 58static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
 59{
 60	u8 cmode;
 61	int err;
 62
 63	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
 64	if (err) {
 65		dev_err(chip->dev, "failed to read cmode\n");
 66		return false;
 67	}
 68
 69	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
 70	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
 71	    (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
 72		return true;
 73
 74	return false;
 75}
 76
 77int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
 78{
 79	int err;
 80
 81	if (mv88e6352_port_has_serdes(chip, port)) {
 82		err = mv88e6352_serdes_power_set(chip, on);
 83		if (err < 0)
 84			return err;
 85	}
 86
 87	return 0;
 88}
 89
 90struct mv88e6352_serdes_hw_stat {
 91	char string[ETH_GSTRING_LEN];
 92	int sizeof_stat;
 93	int reg;
 94};
 95
 96static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
 97	{ "serdes_fibre_rx_error", 16, 21 },
 98	{ "serdes_PRBS_error", 32, 24 },
 99};
100
101int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
102{
103	if (mv88e6352_port_has_serdes(chip, port))
104		return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
105
106	return 0;
107}
108
109int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
110				 int port, uint8_t *data)
111{
112	struct mv88e6352_serdes_hw_stat *stat;
113	int i;
114
115	if (!mv88e6352_port_has_serdes(chip, port))
116		return 0;
117
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,
121		       ETH_GSTRING_LEN);
122	}
123	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
124}
125
126static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
127					  struct mv88e6352_serdes_hw_stat *stat)
128{
129	u64 val = 0;
130	u16 reg;
131	int err;
132
133	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
134	if (err) {
135		dev_err(chip->dev, "failed to read statistic\n");
136		return 0;
137	}
138
139	val = reg;
140
141	if (stat->sizeof_stat == 32) {
142		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
143		if (err) {
144			dev_err(chip->dev, "failed to read statistic\n");
145			return 0;
146		}
147		val = val << 16 | reg;
148	}
149
150	return val;
151}
152
153int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
154			       uint64_t *data)
155{
156	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
157	struct mv88e6352_serdes_hw_stat *stat;
158	u64 value;
159	int i;
160
161	if (!mv88e6352_port_has_serdes(chip, port))
162		return 0;
163
164	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
165		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
166
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];
172	}
173
174	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
175}
176
177/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
178static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
179{
180	u16 val, new_val;
181	int reg_c45;
182	int err;
183
184	reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
185		MV88E6390_PCS_CONTROL_1;
186	err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
187	if (err)
188		return err;
189
190	if (on)
191		new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
192				  MV88E6390_PCS_CONTROL_1_LOOPBACK |
193				  MV88E6390_PCS_CONTROL_1_PDOWN);
194	else
195		new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
196
197	if (val != new_val)
198		err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
199
200	return err;
201}
202
203/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
204static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr,
205				  bool on)
206{
207	u16 val, new_val;
208	int reg_c45;
209	int err;
210
211	reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
212		MV88E6390_SGMII_CONTROL;
213	err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
214	if (err)
215		return err;
216
217	if (on)
218		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
219				  MV88E6390_SGMII_CONTROL_LOOPBACK |
220				  MV88E6390_SGMII_CONTROL_PDOWN);
221	else
222		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
223
224	if (val != new_val)
225		err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val);
226
227	return err;
228}
229
230static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode,
231				  int port_donor, int lane, bool rxaui, bool on)
232{
233	int err;
234	u8 cmode_donor;
235
236	err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor);
237	if (err)
238		return err;
239
240	switch (cmode_donor) {
241	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
242		if (!rxaui)
243			break;
244		/* Fall through */
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);
251	}
252	return 0;
253}
254
255static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode,
256				  bool on)
257{
258	switch (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);
266	}
267
268	return 0;
269}
270
271static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode,
272				   bool on)
273{
274	switch (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);
282	}
283
284	return 0;
285}
286
287int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
288{
289	u8 cmode;
290	int err;
291
292	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
293	if (err)
294		return err;
295
296	switch (port) {
297	case 2:
298		return mv88e6390_serdes_lower(chip, cmode, 9,
299					      MV88E6390_PORT9_LANE1,
300					      false, on);
301	case 3:
302		return mv88e6390_serdes_lower(chip, cmode, 9,
303					      MV88E6390_PORT9_LANE2,
304					      true, on);
305	case 4:
306		return mv88e6390_serdes_lower(chip, cmode, 9,
307					      MV88E6390_PORT9_LANE3,
308					      true, on);
309	case 5:
310		return mv88e6390_serdes_lower(chip, cmode, 10,
311					      MV88E6390_PORT10_LANE1,
312					      false, on);
313	case 6:
314		return mv88e6390_serdes_lower(chip, cmode, 10,
315					      MV88E6390_PORT10_LANE2,
316					      true, on);
317	case 7:
318		return mv88e6390_serdes_lower(chip, cmode, 10,
319					      MV88E6390_PORT10_LANE3,
320					      true, on);
321	case 9:
322		return mv88e6390_serdes_port9(chip, cmode, on);
323	case 10:
324		return mv88e6390_serdes_port10(chip, cmode, on);
325	}
326
327	return 0;
328}