12c81bfd5SHuy Nguyen /*
22c81bfd5SHuy Nguyen  * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
32c81bfd5SHuy Nguyen  *
42c81bfd5SHuy Nguyen  * This software is available to you under a choice of one of two
52c81bfd5SHuy Nguyen  * licenses.  You may choose to be licensed under the terms of the GNU
62c81bfd5SHuy Nguyen  * General Public License (GPL) Version 2, available from the file
72c81bfd5SHuy Nguyen  * COPYING in the main directory of this source tree, or the
82c81bfd5SHuy Nguyen  * OpenIB.org BSD license below:
92c81bfd5SHuy Nguyen  *
102c81bfd5SHuy Nguyen  *     Redistribution and use in source and binary forms, with or
112c81bfd5SHuy Nguyen  *     without modification, are permitted provided that the following
122c81bfd5SHuy Nguyen  *     conditions are met:
132c81bfd5SHuy Nguyen  *
142c81bfd5SHuy Nguyen  *      - Redistributions of source code must retain the above
152c81bfd5SHuy Nguyen  *        copyright notice, this list of conditions and the following
162c81bfd5SHuy Nguyen  *        disclaimer.
172c81bfd5SHuy Nguyen  *
182c81bfd5SHuy Nguyen  *      - Redistributions in binary form must reproduce the above
192c81bfd5SHuy Nguyen  *        copyright notice, this list of conditions and the following
202c81bfd5SHuy Nguyen  *        disclaimer in the documentation and/or other materials
212c81bfd5SHuy Nguyen  *        provided with the distribution.
222c81bfd5SHuy Nguyen  *
232c81bfd5SHuy Nguyen  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
242c81bfd5SHuy Nguyen  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
252c81bfd5SHuy Nguyen  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
262c81bfd5SHuy Nguyen  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
272c81bfd5SHuy Nguyen  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
282c81bfd5SHuy Nguyen  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
292c81bfd5SHuy Nguyen  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
302c81bfd5SHuy Nguyen  * SOFTWARE.
312c81bfd5SHuy Nguyen  */
322c81bfd5SHuy Nguyen 
332c81bfd5SHuy Nguyen #include "port.h"
342c81bfd5SHuy Nguyen 
352c81bfd5SHuy Nguyen /* speed in units of 1Mb */
362c81bfd5SHuy Nguyen static const u32 mlx5e_link_speed[MLX5E_LINK_MODES_NUMBER] = {
372c81bfd5SHuy Nguyen 	[MLX5E_1000BASE_CX_SGMII] = 1000,
382c81bfd5SHuy Nguyen 	[MLX5E_1000BASE_KX]       = 1000,
392c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_CX4]       = 10000,
402c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_KX4]       = 10000,
412c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_KR]        = 10000,
422c81bfd5SHuy Nguyen 	[MLX5E_20GBASE_KR2]       = 20000,
432c81bfd5SHuy Nguyen 	[MLX5E_40GBASE_CR4]       = 40000,
442c81bfd5SHuy Nguyen 	[MLX5E_40GBASE_KR4]       = 40000,
452c81bfd5SHuy Nguyen 	[MLX5E_56GBASE_R4]        = 56000,
462c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_CR]        = 10000,
472c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_SR]        = 10000,
482c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_ER]        = 10000,
492c81bfd5SHuy Nguyen 	[MLX5E_40GBASE_SR4]       = 40000,
502c81bfd5SHuy Nguyen 	[MLX5E_40GBASE_LR4]       = 40000,
512c81bfd5SHuy Nguyen 	[MLX5E_50GBASE_SR2]       = 50000,
522c81bfd5SHuy Nguyen 	[MLX5E_100GBASE_CR4]      = 100000,
532c81bfd5SHuy Nguyen 	[MLX5E_100GBASE_SR4]      = 100000,
542c81bfd5SHuy Nguyen 	[MLX5E_100GBASE_KR4]      = 100000,
552c81bfd5SHuy Nguyen 	[MLX5E_100GBASE_LR4]      = 100000,
562c81bfd5SHuy Nguyen 	[MLX5E_100BASE_TX]        = 100,
572c81bfd5SHuy Nguyen 	[MLX5E_1000BASE_T]        = 1000,
582c81bfd5SHuy Nguyen 	[MLX5E_10GBASE_T]         = 10000,
592c81bfd5SHuy Nguyen 	[MLX5E_25GBASE_CR]        = 25000,
602c81bfd5SHuy Nguyen 	[MLX5E_25GBASE_KR]        = 25000,
612c81bfd5SHuy Nguyen 	[MLX5E_25GBASE_SR]        = 25000,
622c81bfd5SHuy Nguyen 	[MLX5E_50GBASE_CR2]       = 50000,
632c81bfd5SHuy Nguyen 	[MLX5E_50GBASE_KR2]       = 50000,
642c81bfd5SHuy Nguyen };
652c81bfd5SHuy Nguyen 
66bc4e12ffSAya Levin int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port,
67bc4e12ffSAya Levin 			      struct mlx5e_port_eth_proto *eproto)
68bc4e12ffSAya Levin {
69bc4e12ffSAya Levin 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
70bc4e12ffSAya Levin 	int err;
71bc4e12ffSAya Levin 
72bc4e12ffSAya Levin 	if (!eproto)
73bc4e12ffSAya Levin 		return -EINVAL;
74bc4e12ffSAya Levin 
75bc4e12ffSAya Levin 	err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port);
76bc4e12ffSAya Levin 	if (err)
77bc4e12ffSAya Levin 		return err;
78bc4e12ffSAya Levin 
79bc4e12ffSAya Levin 	eproto->cap   = MLX5_GET(ptys_reg, out, eth_proto_capability);
80bc4e12ffSAya Levin 	eproto->admin = MLX5_GET(ptys_reg, out, eth_proto_admin);
81bc4e12ffSAya Levin 	eproto->oper  = MLX5_GET(ptys_reg, out, eth_proto_oper);
82bc4e12ffSAya Levin 	return 0;
83bc4e12ffSAya Levin }
84bc4e12ffSAya Levin 
85bc4e12ffSAya Levin void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
86bc4e12ffSAya Levin 				 u8 *an_disable_cap, u8 *an_disable_admin)
87bc4e12ffSAya Levin {
88bc4e12ffSAya Levin 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
89bc4e12ffSAya Levin 
90bc4e12ffSAya Levin 	*an_status = 0;
91bc4e12ffSAya Levin 	*an_disable_cap = 0;
92bc4e12ffSAya Levin 	*an_disable_admin = 0;
93bc4e12ffSAya Levin 
94bc4e12ffSAya Levin 	if (mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, 1))
95bc4e12ffSAya Levin 		return;
96bc4e12ffSAya Levin 
97bc4e12ffSAya Levin 	*an_status = MLX5_GET(ptys_reg, out, an_status);
98bc4e12ffSAya Levin 	*an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
99bc4e12ffSAya Levin 	*an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
100bc4e12ffSAya Levin }
101bc4e12ffSAya Levin 
102bc4e12ffSAya Levin int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
103bc4e12ffSAya Levin 			   u32 proto_admin)
104bc4e12ffSAya Levin {
105bc4e12ffSAya Levin 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
106bc4e12ffSAya Levin 	u32 in[MLX5_ST_SZ_DW(ptys_reg)];
107bc4e12ffSAya Levin 	u8 an_disable_admin;
108bc4e12ffSAya Levin 	u8 an_disable_cap;
109bc4e12ffSAya Levin 	u8 an_status;
110bc4e12ffSAya Levin 
111bc4e12ffSAya Levin 	mlx5_port_query_eth_autoneg(dev, &an_status, &an_disable_cap,
112bc4e12ffSAya Levin 				    &an_disable_admin);
113bc4e12ffSAya Levin 	if (!an_disable_cap && an_disable)
114bc4e12ffSAya Levin 		return -EPERM;
115bc4e12ffSAya Levin 
116bc4e12ffSAya Levin 	memset(in, 0, sizeof(in));
117bc4e12ffSAya Levin 
118bc4e12ffSAya Levin 	MLX5_SET(ptys_reg, in, local_port, 1);
119bc4e12ffSAya Levin 	MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
120bc4e12ffSAya Levin 	MLX5_SET(ptys_reg, in, proto_mask, MLX5_PTYS_EN);
121bc4e12ffSAya Levin 	MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
122bc4e12ffSAya Levin 
123bc4e12ffSAya Levin 	return mlx5_core_access_reg(dev, in, sizeof(in), out,
124bc4e12ffSAya Levin 			    sizeof(out), MLX5_REG_PTYS, 0, 1);
125bc4e12ffSAya Levin }
126bc4e12ffSAya Levin 
1272c81bfd5SHuy Nguyen u32 mlx5e_port_ptys2speed(u32 eth_proto_oper)
1282c81bfd5SHuy Nguyen {
1292c81bfd5SHuy Nguyen 	unsigned long temp = eth_proto_oper;
1302c81bfd5SHuy Nguyen 	u32 speed = 0;
1312c81bfd5SHuy Nguyen 	int i;
1322c81bfd5SHuy Nguyen 
1332c81bfd5SHuy Nguyen 	i = find_first_bit(&temp, MLX5E_LINK_MODES_NUMBER);
1342c81bfd5SHuy Nguyen 	if (i < MLX5E_LINK_MODES_NUMBER)
1352c81bfd5SHuy Nguyen 		speed = mlx5e_link_speed[i];
1362c81bfd5SHuy Nguyen 
1372c81bfd5SHuy Nguyen 	return speed;
1382c81bfd5SHuy Nguyen }
1392c81bfd5SHuy Nguyen 
1402c81bfd5SHuy Nguyen int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
1412c81bfd5SHuy Nguyen {
142bc4e12ffSAya Levin 	struct mlx5e_port_eth_proto eproto;
1432c81bfd5SHuy Nguyen 	int err;
1442c81bfd5SHuy Nguyen 
145bc4e12ffSAya Levin 	err = mlx5_port_query_eth_proto(mdev, 1, &eproto);
1462c81bfd5SHuy Nguyen 	if (err)
1472c81bfd5SHuy Nguyen 		return err;
1482c81bfd5SHuy Nguyen 
149bc4e12ffSAya Levin 	*speed = mlx5e_port_ptys2speed(eproto.oper);
15064e28334SShay Agroskin 	if (!(*speed))
1512c81bfd5SHuy Nguyen 		err = -EINVAL;
1522c81bfd5SHuy Nguyen 
1532c81bfd5SHuy Nguyen 	return err;
1542c81bfd5SHuy Nguyen }
1552c81bfd5SHuy Nguyen 
1562c81bfd5SHuy Nguyen int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
1572c81bfd5SHuy Nguyen {
158bc4e12ffSAya Levin 	struct mlx5e_port_eth_proto eproto;
1592c81bfd5SHuy Nguyen 	u32 max_speed = 0;
1602c81bfd5SHuy Nguyen 	int err;
1612c81bfd5SHuy Nguyen 	int i;
1622c81bfd5SHuy Nguyen 
163bc4e12ffSAya Levin 	err = mlx5_port_query_eth_proto(mdev, 1, &eproto);
1642c81bfd5SHuy Nguyen 	if (err)
1652c81bfd5SHuy Nguyen 		return err;
1662c81bfd5SHuy Nguyen 
1672c81bfd5SHuy Nguyen 	for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i)
168bc4e12ffSAya Levin 		if (eproto.cap & MLX5E_PROT_MASK(i))
1692c81bfd5SHuy Nguyen 			max_speed = max(max_speed, mlx5e_link_speed[i]);
1702c81bfd5SHuy Nguyen 
1712c81bfd5SHuy Nguyen 	*speed = max_speed;
1722c81bfd5SHuy Nguyen 	return 0;
1732c81bfd5SHuy Nguyen }
1742c81bfd5SHuy Nguyen 
1752c81bfd5SHuy Nguyen u32 mlx5e_port_speed2linkmodes(u32 speed)
1762c81bfd5SHuy Nguyen {
1772c81bfd5SHuy Nguyen 	u32 link_modes = 0;
1782c81bfd5SHuy Nguyen 	int i;
1792c81bfd5SHuy Nguyen 
1802c81bfd5SHuy Nguyen 	for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i) {
1812c81bfd5SHuy Nguyen 		if (mlx5e_link_speed[i] == speed)
1822c81bfd5SHuy Nguyen 			link_modes |= MLX5E_PROT_MASK(i);
1832c81bfd5SHuy Nguyen 	}
1842c81bfd5SHuy Nguyen 
1852c81bfd5SHuy Nguyen 	return link_modes;
1862c81bfd5SHuy Nguyen }
18750b4a3c2SHuy Nguyen 
18850b4a3c2SHuy Nguyen int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out)
18950b4a3c2SHuy Nguyen {
19050b4a3c2SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
19150b4a3c2SHuy Nguyen 	void *in;
19250b4a3c2SHuy Nguyen 	int err;
19350b4a3c2SHuy Nguyen 
19450b4a3c2SHuy Nguyen 	in = kzalloc(sz, GFP_KERNEL);
19550b4a3c2SHuy Nguyen 	if (!in)
19650b4a3c2SHuy Nguyen 		return -ENOMEM;
19750b4a3c2SHuy Nguyen 
19850b4a3c2SHuy Nguyen 	MLX5_SET(pbmc_reg, in, local_port, 1);
19950b4a3c2SHuy Nguyen 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 0);
20050b4a3c2SHuy Nguyen 
20150b4a3c2SHuy Nguyen 	kfree(in);
20250b4a3c2SHuy Nguyen 	return err;
20350b4a3c2SHuy Nguyen }
20450b4a3c2SHuy Nguyen 
20550b4a3c2SHuy Nguyen int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in)
20650b4a3c2SHuy Nguyen {
20750b4a3c2SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
20850b4a3c2SHuy Nguyen 	void *out;
20950b4a3c2SHuy Nguyen 	int err;
21050b4a3c2SHuy Nguyen 
21150b4a3c2SHuy Nguyen 	out = kzalloc(sz, GFP_KERNEL);
21250b4a3c2SHuy Nguyen 	if (!out)
21350b4a3c2SHuy Nguyen 		return -ENOMEM;
21450b4a3c2SHuy Nguyen 
21550b4a3c2SHuy Nguyen 	MLX5_SET(pbmc_reg, in, local_port, 1);
21650b4a3c2SHuy Nguyen 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 1);
21750b4a3c2SHuy Nguyen 
21850b4a3c2SHuy Nguyen 	kfree(out);
21950b4a3c2SHuy Nguyen 	return err;
22050b4a3c2SHuy Nguyen }
22150b4a3c2SHuy Nguyen 
22250b4a3c2SHuy Nguyen /* buffer[i]: buffer that priority i mapped to */
22350b4a3c2SHuy Nguyen int mlx5e_port_query_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
22450b4a3c2SHuy Nguyen {
22550b4a3c2SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
22650b4a3c2SHuy Nguyen 	u32 prio_x_buff;
22750b4a3c2SHuy Nguyen 	void *out;
22850b4a3c2SHuy Nguyen 	void *in;
22950b4a3c2SHuy Nguyen 	int prio;
23050b4a3c2SHuy Nguyen 	int err;
23150b4a3c2SHuy Nguyen 
23250b4a3c2SHuy Nguyen 	in = kzalloc(sz, GFP_KERNEL);
23350b4a3c2SHuy Nguyen 	out = kzalloc(sz, GFP_KERNEL);
23450b4a3c2SHuy Nguyen 	if (!in || !out) {
23550b4a3c2SHuy Nguyen 		err = -ENOMEM;
23650b4a3c2SHuy Nguyen 		goto out;
23750b4a3c2SHuy Nguyen 	}
23850b4a3c2SHuy Nguyen 
23950b4a3c2SHuy Nguyen 	MLX5_SET(pptb_reg, in, local_port, 1);
24050b4a3c2SHuy Nguyen 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
24150b4a3c2SHuy Nguyen 	if (err)
24250b4a3c2SHuy Nguyen 		goto out;
24350b4a3c2SHuy Nguyen 
24450b4a3c2SHuy Nguyen 	prio_x_buff = MLX5_GET(pptb_reg, out, prio_x_buff);
24550b4a3c2SHuy Nguyen 	for (prio = 0; prio < 8; prio++) {
24650b4a3c2SHuy Nguyen 		buffer[prio] = (u8)(prio_x_buff >> (4 * prio)) & 0xF;
24750b4a3c2SHuy Nguyen 		mlx5_core_dbg(mdev, "prio %d, buffer %d\n", prio, buffer[prio]);
24850b4a3c2SHuy Nguyen 	}
24950b4a3c2SHuy Nguyen out:
25050b4a3c2SHuy Nguyen 	kfree(in);
25150b4a3c2SHuy Nguyen 	kfree(out);
25250b4a3c2SHuy Nguyen 	return err;
25350b4a3c2SHuy Nguyen }
25450b4a3c2SHuy Nguyen 
25550b4a3c2SHuy Nguyen int mlx5e_port_set_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
25650b4a3c2SHuy Nguyen {
25750b4a3c2SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
25850b4a3c2SHuy Nguyen 	u32 prio_x_buff;
25950b4a3c2SHuy Nguyen 	void *out;
26050b4a3c2SHuy Nguyen 	void *in;
26150b4a3c2SHuy Nguyen 	int prio;
26250b4a3c2SHuy Nguyen 	int err;
26350b4a3c2SHuy Nguyen 
26450b4a3c2SHuy Nguyen 	in = kzalloc(sz, GFP_KERNEL);
26550b4a3c2SHuy Nguyen 	out = kzalloc(sz, GFP_KERNEL);
26650b4a3c2SHuy Nguyen 	if (!in || !out) {
26750b4a3c2SHuy Nguyen 		err = -ENOMEM;
26850b4a3c2SHuy Nguyen 		goto out;
26950b4a3c2SHuy Nguyen 	}
27050b4a3c2SHuy Nguyen 
27150b4a3c2SHuy Nguyen 	/* First query the pptb register */
27250b4a3c2SHuy Nguyen 	MLX5_SET(pptb_reg, in, local_port, 1);
27350b4a3c2SHuy Nguyen 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
27450b4a3c2SHuy Nguyen 	if (err)
27550b4a3c2SHuy Nguyen 		goto out;
27650b4a3c2SHuy Nguyen 
27750b4a3c2SHuy Nguyen 	memcpy(in, out, sz);
27850b4a3c2SHuy Nguyen 	MLX5_SET(pptb_reg, in, local_port, 1);
27950b4a3c2SHuy Nguyen 
28050b4a3c2SHuy Nguyen 	/* Update the pm and prio_x_buff */
28150b4a3c2SHuy Nguyen 	MLX5_SET(pptb_reg, in, pm, 0xFF);
28250b4a3c2SHuy Nguyen 
28350b4a3c2SHuy Nguyen 	prio_x_buff = 0;
28450b4a3c2SHuy Nguyen 	for (prio = 0; prio < 8; prio++)
28550b4a3c2SHuy Nguyen 		prio_x_buff |= (buffer[prio] << (4 * prio));
28650b4a3c2SHuy Nguyen 	MLX5_SET(pptb_reg, in, prio_x_buff, prio_x_buff);
28750b4a3c2SHuy Nguyen 
28850b4a3c2SHuy Nguyen 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 1);
28950b4a3c2SHuy Nguyen 
29050b4a3c2SHuy Nguyen out:
29150b4a3c2SHuy Nguyen 	kfree(in);
29250b4a3c2SHuy Nguyen 	kfree(out);
29350b4a3c2SHuy Nguyen 	return err;
29450b4a3c2SHuy Nguyen }
2952095b264SShay Agroskin 
2962095b264SShay Agroskin static u32 fec_supported_speeds[] = {
2972095b264SShay Agroskin 	10000,
2982095b264SShay Agroskin 	40000,
2992095b264SShay Agroskin 	25000,
3002095b264SShay Agroskin 	50000,
3012095b264SShay Agroskin 	56000,
3022095b264SShay Agroskin 	100000
3032095b264SShay Agroskin };
3042095b264SShay Agroskin 
3052095b264SShay Agroskin #define MLX5E_FEC_SUPPORTED_SPEEDS ARRAY_SIZE(fec_supported_speeds)
3062095b264SShay Agroskin 
3072095b264SShay Agroskin /* get/set FEC admin field for a given speed */
3082095b264SShay Agroskin static int mlx5e_fec_admin_field(u32 *pplm,
3092095b264SShay Agroskin 				 u8 *fec_policy,
3102095b264SShay Agroskin 				 bool write,
3112095b264SShay Agroskin 				 u32 speed)
3122095b264SShay Agroskin {
3132095b264SShay Agroskin 	switch (speed) {
3142095b264SShay Agroskin 	case 10000:
3152095b264SShay Agroskin 	case 40000:
3162095b264SShay Agroskin 		if (!write)
3172095b264SShay Agroskin 			*fec_policy = MLX5_GET(pplm_reg, pplm,
318febd72f2SShay Agroskin 					       fec_override_admin_10g_40g);
3192095b264SShay Agroskin 		else
3202095b264SShay Agroskin 			MLX5_SET(pplm_reg, pplm,
3212095b264SShay Agroskin 				 fec_override_admin_10g_40g, *fec_policy);
3222095b264SShay Agroskin 		break;
3232095b264SShay Agroskin 	case 25000:
3242095b264SShay Agroskin 		if (!write)
3252095b264SShay Agroskin 			*fec_policy = MLX5_GET(pplm_reg, pplm,
3262095b264SShay Agroskin 					       fec_override_admin_25g);
3272095b264SShay Agroskin 		else
3282095b264SShay Agroskin 			MLX5_SET(pplm_reg, pplm,
3292095b264SShay Agroskin 				 fec_override_admin_25g, *fec_policy);
3302095b264SShay Agroskin 		break;
3312095b264SShay Agroskin 	case 50000:
3322095b264SShay Agroskin 		if (!write)
3332095b264SShay Agroskin 			*fec_policy = MLX5_GET(pplm_reg, pplm,
3342095b264SShay Agroskin 					       fec_override_admin_50g);
3352095b264SShay Agroskin 		else
3362095b264SShay Agroskin 			MLX5_SET(pplm_reg, pplm,
3372095b264SShay Agroskin 				 fec_override_admin_50g, *fec_policy);
3382095b264SShay Agroskin 		break;
3392095b264SShay Agroskin 	case 56000:
3402095b264SShay Agroskin 		if (!write)
3412095b264SShay Agroskin 			*fec_policy = MLX5_GET(pplm_reg, pplm,
3422095b264SShay Agroskin 					       fec_override_admin_56g);
3432095b264SShay Agroskin 		else
3442095b264SShay Agroskin 			MLX5_SET(pplm_reg, pplm,
3452095b264SShay Agroskin 				 fec_override_admin_56g, *fec_policy);
3462095b264SShay Agroskin 		break;
3472095b264SShay Agroskin 	case 100000:
3482095b264SShay Agroskin 		if (!write)
3492095b264SShay Agroskin 			*fec_policy = MLX5_GET(pplm_reg, pplm,
3502095b264SShay Agroskin 					       fec_override_admin_100g);
3512095b264SShay Agroskin 		else
3522095b264SShay Agroskin 			MLX5_SET(pplm_reg, pplm,
3532095b264SShay Agroskin 				 fec_override_admin_100g, *fec_policy);
3542095b264SShay Agroskin 		break;
3552095b264SShay Agroskin 	default:
3562095b264SShay Agroskin 		return -EINVAL;
3572095b264SShay Agroskin 	}
3582095b264SShay Agroskin 	return 0;
3592095b264SShay Agroskin }
3602095b264SShay Agroskin 
3612095b264SShay Agroskin /* returns FEC capabilities for a given speed */
3622095b264SShay Agroskin static int mlx5e_get_fec_cap_field(u32 *pplm,
3632095b264SShay Agroskin 				   u8 *fec_cap,
3642095b264SShay Agroskin 				   u32 speed)
3652095b264SShay Agroskin {
3662095b264SShay Agroskin 	switch (speed) {
3672095b264SShay Agroskin 	case 10000:
3682095b264SShay Agroskin 	case 40000:
3692095b264SShay Agroskin 		*fec_cap = MLX5_GET(pplm_reg, pplm,
370febd72f2SShay Agroskin 				    fec_override_cap_10g_40g);
3712095b264SShay Agroskin 		break;
3722095b264SShay Agroskin 	case 25000:
3732095b264SShay Agroskin 		*fec_cap = MLX5_GET(pplm_reg, pplm,
3742095b264SShay Agroskin 				    fec_override_cap_25g);
3752095b264SShay Agroskin 		break;
3762095b264SShay Agroskin 	case 50000:
3772095b264SShay Agroskin 		*fec_cap = MLX5_GET(pplm_reg, pplm,
3782095b264SShay Agroskin 				    fec_override_cap_50g);
3792095b264SShay Agroskin 		break;
3802095b264SShay Agroskin 	case 56000:
3812095b264SShay Agroskin 		*fec_cap = MLX5_GET(pplm_reg, pplm,
3822095b264SShay Agroskin 				    fec_override_cap_56g);
3832095b264SShay Agroskin 		break;
3842095b264SShay Agroskin 	case 100000:
3852095b264SShay Agroskin 		*fec_cap = MLX5_GET(pplm_reg, pplm,
3862095b264SShay Agroskin 				    fec_override_cap_100g);
3872095b264SShay Agroskin 		break;
3882095b264SShay Agroskin 	default:
3892095b264SShay Agroskin 		return -EINVAL;
3902095b264SShay Agroskin 	}
3912095b264SShay Agroskin 	return 0;
3922095b264SShay Agroskin }
3932095b264SShay Agroskin 
3942095b264SShay Agroskin int mlx5e_get_fec_caps(struct mlx5_core_dev *dev, u8 *fec_caps)
3952095b264SShay Agroskin {
3962095b264SShay Agroskin 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
3972095b264SShay Agroskin 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
3982095b264SShay Agroskin 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
3992095b264SShay Agroskin 	u32 current_fec_speed;
4002095b264SShay Agroskin 	int err;
4012095b264SShay Agroskin 
4022095b264SShay Agroskin 	if (!MLX5_CAP_GEN(dev, pcam_reg))
4032095b264SShay Agroskin 		return -EOPNOTSUPP;
4042095b264SShay Agroskin 
4052095b264SShay Agroskin 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
4062095b264SShay Agroskin 		return -EOPNOTSUPP;
4072095b264SShay Agroskin 
4082095b264SShay Agroskin 	MLX5_SET(pplm_reg, in, local_port, 1);
4092095b264SShay Agroskin 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
4102095b264SShay Agroskin 	if (err)
4112095b264SShay Agroskin 		return err;
4122095b264SShay Agroskin 
4132095b264SShay Agroskin 	err = mlx5e_port_linkspeed(dev, &current_fec_speed);
4142095b264SShay Agroskin 	if (err)
4152095b264SShay Agroskin 		return err;
4162095b264SShay Agroskin 
4172095b264SShay Agroskin 	return mlx5e_get_fec_cap_field(out, fec_caps, current_fec_speed);
4182095b264SShay Agroskin }
4192095b264SShay Agroskin 
4202095b264SShay Agroskin int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
4212095b264SShay Agroskin 		       u8 *fec_configured_mode)
4222095b264SShay Agroskin {
4232095b264SShay Agroskin 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
4242095b264SShay Agroskin 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
4252095b264SShay Agroskin 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
4262095b264SShay Agroskin 	u32 link_speed;
4272095b264SShay Agroskin 	int err;
4282095b264SShay Agroskin 
4292095b264SShay Agroskin 	if (!MLX5_CAP_GEN(dev, pcam_reg))
4302095b264SShay Agroskin 		return -EOPNOTSUPP;
4312095b264SShay Agroskin 
4322095b264SShay Agroskin 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
4332095b264SShay Agroskin 		return -EOPNOTSUPP;
4342095b264SShay Agroskin 
4352095b264SShay Agroskin 	MLX5_SET(pplm_reg, in, local_port, 1);
4362095b264SShay Agroskin 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
4372095b264SShay Agroskin 	if (err)
4382095b264SShay Agroskin 		return err;
4392095b264SShay Agroskin 
4402095b264SShay Agroskin 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
4412095b264SShay Agroskin 
4422095b264SShay Agroskin 	if (!fec_configured_mode)
4432095b264SShay Agroskin 		return 0;
4442095b264SShay Agroskin 
4452095b264SShay Agroskin 	err = mlx5e_port_linkspeed(dev, &link_speed);
4462095b264SShay Agroskin 	if (err)
4472095b264SShay Agroskin 		return err;
4482095b264SShay Agroskin 
4492095b264SShay Agroskin 	return mlx5e_fec_admin_field(out, fec_configured_mode, 0, link_speed);
4502095b264SShay Agroskin }
4512095b264SShay Agroskin 
4522095b264SShay Agroskin int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy)
4532095b264SShay Agroskin {
4549cdeaab3SShay Agroskin 	u8 fec_policy_nofec = BIT(MLX5E_FEC_NOFEC);
4552095b264SShay Agroskin 	bool fec_mode_not_supp_in_speed = false;
4562095b264SShay Agroskin 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
4572095b264SShay Agroskin 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
4582095b264SShay Agroskin 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
4599cdeaab3SShay Agroskin 	u8 fec_policy_auto = 0;
4602095b264SShay Agroskin 	u8 fec_caps = 0;
4612095b264SShay Agroskin 	int err;
4622095b264SShay Agroskin 	int i;
4632095b264SShay Agroskin 
4642095b264SShay Agroskin 	if (!MLX5_CAP_GEN(dev, pcam_reg))
4652095b264SShay Agroskin 		return -EOPNOTSUPP;
4662095b264SShay Agroskin 
4672095b264SShay Agroskin 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
4682095b264SShay Agroskin 		return -EOPNOTSUPP;
4692095b264SShay Agroskin 
4702095b264SShay Agroskin 	MLX5_SET(pplm_reg, in, local_port, 1);
4712095b264SShay Agroskin 	err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
4722095b264SShay Agroskin 	if (err)
4732095b264SShay Agroskin 		return err;
4742095b264SShay Agroskin 
4759cdeaab3SShay Agroskin 	MLX5_SET(pplm_reg, out, local_port, 1);
4762095b264SShay Agroskin 
4779cdeaab3SShay Agroskin 	for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS; i++) {
4782095b264SShay Agroskin 		mlx5e_get_fec_cap_field(out, &fec_caps, fec_supported_speeds[i]);
4799cdeaab3SShay Agroskin 		/* policy supported for link speed, or policy is auto */
4809cdeaab3SShay Agroskin 		if (fec_caps & fec_policy || fec_policy == fec_policy_auto) {
4819cdeaab3SShay Agroskin 			mlx5e_fec_admin_field(out, &fec_policy, 1,
4822095b264SShay Agroskin 					      fec_supported_speeds[i]);
4832095b264SShay Agroskin 		} else {
4849cdeaab3SShay Agroskin 			/* turn off FEC if supported. Else, leave it the same */
4859cdeaab3SShay Agroskin 			if (fec_caps & fec_policy_nofec)
4869cdeaab3SShay Agroskin 				mlx5e_fec_admin_field(out, &fec_policy_nofec, 1,
4872095b264SShay Agroskin 						      fec_supported_speeds[i]);
4882095b264SShay Agroskin 			fec_mode_not_supp_in_speed = true;
4892095b264SShay Agroskin 		}
4902095b264SShay Agroskin 	}
4912095b264SShay Agroskin 
4922095b264SShay Agroskin 	if (fec_mode_not_supp_in_speed)
4932095b264SShay Agroskin 		mlx5_core_dbg(dev,
4942095b264SShay Agroskin 			      "FEC policy 0x%x is not supported for some speeds",
4952095b264SShay Agroskin 			      fec_policy);
4962095b264SShay Agroskin 
4979cdeaab3SShay Agroskin 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
4982095b264SShay Agroskin }
499