10696d608SHuy Nguyen /*
20696d608SHuy Nguyen  * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
30696d608SHuy Nguyen  *
40696d608SHuy Nguyen  * This software is available to you under a choice of one of two
50696d608SHuy Nguyen  * licenses.  You may choose to be licensed under the terms of the GNU
60696d608SHuy Nguyen  * General Public License (GPL) Version 2, available from the file
70696d608SHuy Nguyen  * COPYING in the main directory of this source tree, or the
80696d608SHuy Nguyen  * OpenIB.org BSD license below:
90696d608SHuy Nguyen  *
100696d608SHuy Nguyen  *     Redistribution and use in source and binary forms, with or
110696d608SHuy Nguyen  *     without modification, are permitted provided that the following
120696d608SHuy Nguyen  *     conditions are met:
130696d608SHuy Nguyen  *
140696d608SHuy Nguyen  *      - Redistributions of source code must retain the above
150696d608SHuy Nguyen  *        copyright notice, this list of conditions and the following
160696d608SHuy Nguyen  *        disclaimer.
170696d608SHuy Nguyen  *
180696d608SHuy Nguyen  *      - Redistributions in binary form must reproduce the above
190696d608SHuy Nguyen  *        copyright notice, this list of conditions and the following
200696d608SHuy Nguyen  *        disclaimer in the documentation and/or other materials
210696d608SHuy Nguyen  *        provided with the distribution.
220696d608SHuy Nguyen  *
230696d608SHuy Nguyen  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
240696d608SHuy Nguyen  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
250696d608SHuy Nguyen  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
260696d608SHuy Nguyen  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
270696d608SHuy Nguyen  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
280696d608SHuy Nguyen  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
290696d608SHuy Nguyen  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
300696d608SHuy Nguyen  * SOFTWARE.
310696d608SHuy Nguyen  */
320696d608SHuy Nguyen #include "port_buffer.h"
330696d608SHuy Nguyen 
340696d608SHuy Nguyen int mlx5e_port_query_buffer(struct mlx5e_priv *priv,
350696d608SHuy Nguyen 			    struct mlx5e_port_buffer *port_buffer)
360696d608SHuy Nguyen {
370696d608SHuy Nguyen 	struct mlx5_core_dev *mdev = priv->mdev;
380696d608SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
390696d608SHuy Nguyen 	u32 total_used = 0;
400696d608SHuy Nguyen 	void *buffer;
410696d608SHuy Nguyen 	void *out;
420696d608SHuy Nguyen 	int err;
430696d608SHuy Nguyen 	int i;
440696d608SHuy Nguyen 
450696d608SHuy Nguyen 	out = kzalloc(sz, GFP_KERNEL);
460696d608SHuy Nguyen 	if (!out)
470696d608SHuy Nguyen 		return -ENOMEM;
480696d608SHuy Nguyen 
490696d608SHuy Nguyen 	err = mlx5e_port_query_pbmc(mdev, out);
500696d608SHuy Nguyen 	if (err)
510696d608SHuy Nguyen 		goto out;
520696d608SHuy Nguyen 
530696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
540696d608SHuy Nguyen 		buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]);
550696d608SHuy Nguyen 		port_buffer->buffer[i].lossy =
560696d608SHuy Nguyen 			MLX5_GET(bufferx_reg, buffer, lossy);
570696d608SHuy Nguyen 		port_buffer->buffer[i].epsb =
580696d608SHuy Nguyen 			MLX5_GET(bufferx_reg, buffer, epsb);
590696d608SHuy Nguyen 		port_buffer->buffer[i].size =
600696d608SHuy Nguyen 			MLX5_GET(bufferx_reg, buffer, size) << MLX5E_BUFFER_CELL_SHIFT;
610696d608SHuy Nguyen 		port_buffer->buffer[i].xon =
620696d608SHuy Nguyen 			MLX5_GET(bufferx_reg, buffer, xon_threshold) << MLX5E_BUFFER_CELL_SHIFT;
630696d608SHuy Nguyen 		port_buffer->buffer[i].xoff =
640696d608SHuy Nguyen 			MLX5_GET(bufferx_reg, buffer, xoff_threshold) << MLX5E_BUFFER_CELL_SHIFT;
650696d608SHuy Nguyen 		total_used += port_buffer->buffer[i].size;
660696d608SHuy Nguyen 
670696d608SHuy Nguyen 		mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i,
680696d608SHuy Nguyen 			  port_buffer->buffer[i].size,
690696d608SHuy Nguyen 			  port_buffer->buffer[i].xon,
700696d608SHuy Nguyen 			  port_buffer->buffer[i].xoff,
710696d608SHuy Nguyen 			  port_buffer->buffer[i].epsb,
720696d608SHuy Nguyen 			  port_buffer->buffer[i].lossy);
730696d608SHuy Nguyen 	}
740696d608SHuy Nguyen 
750696d608SHuy Nguyen 	port_buffer->port_buffer_size =
760696d608SHuy Nguyen 		MLX5_GET(pbmc_reg, out, port_buffer_size) << MLX5E_BUFFER_CELL_SHIFT;
770696d608SHuy Nguyen 	port_buffer->spare_buffer_size =
780696d608SHuy Nguyen 		port_buffer->port_buffer_size - total_used;
790696d608SHuy Nguyen 
800696d608SHuy Nguyen 	mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n",
810696d608SHuy Nguyen 		  port_buffer->port_buffer_size,
820696d608SHuy Nguyen 		  port_buffer->spare_buffer_size);
830696d608SHuy Nguyen out:
840696d608SHuy Nguyen 	kfree(out);
850696d608SHuy Nguyen 	return err;
860696d608SHuy Nguyen }
870696d608SHuy Nguyen 
880696d608SHuy Nguyen static int port_set_buffer(struct mlx5e_priv *priv,
890696d608SHuy Nguyen 			   struct mlx5e_port_buffer *port_buffer)
900696d608SHuy Nguyen {
910696d608SHuy Nguyen 	struct mlx5_core_dev *mdev = priv->mdev;
920696d608SHuy Nguyen 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
930696d608SHuy Nguyen 	void *buffer;
940696d608SHuy Nguyen 	void *in;
950696d608SHuy Nguyen 	int err;
960696d608SHuy Nguyen 	int i;
970696d608SHuy Nguyen 
980696d608SHuy Nguyen 	in = kzalloc(sz, GFP_KERNEL);
990696d608SHuy Nguyen 	if (!in)
1000696d608SHuy Nguyen 		return -ENOMEM;
1010696d608SHuy Nguyen 
1020696d608SHuy Nguyen 	err = mlx5e_port_query_pbmc(mdev, in);
1030696d608SHuy Nguyen 	if (err)
1040696d608SHuy Nguyen 		goto out;
1050696d608SHuy Nguyen 
1060696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
1070696d608SHuy Nguyen 		buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]);
1080696d608SHuy Nguyen 
1090696d608SHuy Nguyen 		MLX5_SET(bufferx_reg, buffer, size,
1100696d608SHuy Nguyen 			 port_buffer->buffer[i].size >> MLX5E_BUFFER_CELL_SHIFT);
1110696d608SHuy Nguyen 		MLX5_SET(bufferx_reg, buffer, lossy,
1120696d608SHuy Nguyen 			 port_buffer->buffer[i].lossy);
1130696d608SHuy Nguyen 		MLX5_SET(bufferx_reg, buffer, xoff_threshold,
1140696d608SHuy Nguyen 			 port_buffer->buffer[i].xoff >> MLX5E_BUFFER_CELL_SHIFT);
1150696d608SHuy Nguyen 		MLX5_SET(bufferx_reg, buffer, xon_threshold,
1160696d608SHuy Nguyen 			 port_buffer->buffer[i].xon >> MLX5E_BUFFER_CELL_SHIFT);
1170696d608SHuy Nguyen 	}
1180696d608SHuy Nguyen 
1190696d608SHuy Nguyen 	err = mlx5e_port_set_pbmc(mdev, in);
1200696d608SHuy Nguyen out:
1210696d608SHuy Nguyen 	kfree(in);
1220696d608SHuy Nguyen 	return err;
1230696d608SHuy Nguyen }
1240696d608SHuy Nguyen 
1255ec983e9SHuy Nguyen /* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B])
1265ec983e9SHuy Nguyen  * minimum speed value is 40Gbps
1275ec983e9SHuy Nguyen  */
1280696d608SHuy Nguyen static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu)
1290696d608SHuy Nguyen {
1300696d608SHuy Nguyen 	u32 speed;
1310696d608SHuy Nguyen 	u32 xoff;
1320696d608SHuy Nguyen 	int err;
1330696d608SHuy Nguyen 
1340696d608SHuy Nguyen 	err = mlx5e_port_linkspeed(priv->mdev, &speed);
1355ec983e9SHuy Nguyen 	if (err)
1365ec983e9SHuy Nguyen 		speed = SPEED_40000;
1375ec983e9SHuy Nguyen 	speed = max_t(u32, speed, SPEED_40000);
1380696d608SHuy Nguyen 
1390696d608SHuy Nguyen 	xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100;
1400696d608SHuy Nguyen 
1410696d608SHuy Nguyen 	mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff);
1420696d608SHuy Nguyen 	return xoff;
1430696d608SHuy Nguyen }
1440696d608SHuy Nguyen 
1450696d608SHuy Nguyen static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer,
146e28408e9SHuy Nguyen 				 u32 xoff, unsigned int max_mtu)
1470696d608SHuy Nguyen {
1480696d608SHuy Nguyen 	int i;
1490696d608SHuy Nguyen 
1500696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
1510696d608SHuy Nguyen 		if (port_buffer->buffer[i].lossy) {
1520696d608SHuy Nguyen 			port_buffer->buffer[i].xoff = 0;
1530696d608SHuy Nguyen 			port_buffer->buffer[i].xon  = 0;
1540696d608SHuy Nguyen 			continue;
1550696d608SHuy Nguyen 		}
1560696d608SHuy Nguyen 
1570696d608SHuy Nguyen 		if (port_buffer->buffer[i].size <
158e28408e9SHuy Nguyen 		    (xoff + max_mtu + (1 << MLX5E_BUFFER_CELL_SHIFT)))
1590696d608SHuy Nguyen 			return -ENOMEM;
1600696d608SHuy Nguyen 
1610696d608SHuy Nguyen 		port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff;
162e28408e9SHuy Nguyen 		port_buffer->buffer[i].xon  =
163e28408e9SHuy Nguyen 			port_buffer->buffer[i].xoff - max_mtu;
1640696d608SHuy Nguyen 	}
1650696d608SHuy Nguyen 
1660696d608SHuy Nguyen 	return 0;
1670696d608SHuy Nguyen }
1680696d608SHuy Nguyen 
1690696d608SHuy Nguyen /**
1700696d608SHuy Nguyen  * update_buffer_lossy()
171e28408e9SHuy Nguyen  *   max_mtu: netdev's max_mtu
1720696d608SHuy Nguyen  *   pfc_en: <input> current pfc configuration
1730696d608SHuy Nguyen  *   buffer: <input> current prio to buffer mapping
1740696d608SHuy Nguyen  *   xoff:   <input> xoff value
1750696d608SHuy Nguyen  *   port_buffer: <output> port receive buffer configuration
1760696d608SHuy Nguyen  *   change: <output>
1770696d608SHuy Nguyen  *
1780696d608SHuy Nguyen  *   Update buffer configuration based on pfc configuraiton and priority
1790696d608SHuy Nguyen  *   to buffer mapping.
1800696d608SHuy Nguyen  *   Buffer's lossy bit is changed to:
1810696d608SHuy Nguyen  *     lossless if there is at least one PFC enabled priority mapped to this buffer
1820696d608SHuy Nguyen  *     lossy if all priorities mapped to this buffer are PFC disabled
1830696d608SHuy Nguyen  *
1840696d608SHuy Nguyen  *   Return:
1850696d608SHuy Nguyen  *     Return 0 if no error.
1860696d608SHuy Nguyen  *     Set change to true if buffer configuration is modified.
1870696d608SHuy Nguyen  */
188e28408e9SHuy Nguyen static int update_buffer_lossy(unsigned int max_mtu,
1890696d608SHuy Nguyen 			       u8 pfc_en, u8 *buffer, u32 xoff,
1900696d608SHuy Nguyen 			       struct mlx5e_port_buffer *port_buffer,
1910696d608SHuy Nguyen 			       bool *change)
1920696d608SHuy Nguyen {
1930696d608SHuy Nguyen 	bool changed = false;
1940696d608SHuy Nguyen 	u8 lossy_count;
1950696d608SHuy Nguyen 	u8 prio_count;
1960696d608SHuy Nguyen 	u8 lossy;
1970696d608SHuy Nguyen 	int prio;
1980696d608SHuy Nguyen 	int err;
1990696d608SHuy Nguyen 	int i;
2000696d608SHuy Nguyen 
2010696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
2020696d608SHuy Nguyen 		prio_count = 0;
2030696d608SHuy Nguyen 		lossy_count = 0;
2040696d608SHuy Nguyen 
2050696d608SHuy Nguyen 		for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) {
2060696d608SHuy Nguyen 			if (buffer[prio] != i)
2070696d608SHuy Nguyen 				continue;
2080696d608SHuy Nguyen 
2090696d608SHuy Nguyen 			prio_count++;
2100696d608SHuy Nguyen 			lossy_count += !(pfc_en & (1 << prio));
2110696d608SHuy Nguyen 		}
2120696d608SHuy Nguyen 
2130696d608SHuy Nguyen 		if (lossy_count == prio_count)
2140696d608SHuy Nguyen 			lossy = 1;
2150696d608SHuy Nguyen 		else /* lossy_count < prio_count */
2160696d608SHuy Nguyen 			lossy = 0;
2170696d608SHuy Nguyen 
2180696d608SHuy Nguyen 		if (lossy != port_buffer->buffer[i].lossy) {
2190696d608SHuy Nguyen 			port_buffer->buffer[i].lossy = lossy;
2200696d608SHuy Nguyen 			changed = true;
2210696d608SHuy Nguyen 		}
2220696d608SHuy Nguyen 	}
2230696d608SHuy Nguyen 
2240696d608SHuy Nguyen 	if (changed) {
225e28408e9SHuy Nguyen 		err = update_xoff_threshold(port_buffer, xoff, max_mtu);
2260696d608SHuy Nguyen 		if (err)
2270696d608SHuy Nguyen 			return err;
2280696d608SHuy Nguyen 
2290696d608SHuy Nguyen 		*change = true;
2300696d608SHuy Nguyen 	}
2310696d608SHuy Nguyen 
2320696d608SHuy Nguyen 	return 0;
2330696d608SHuy Nguyen }
2340696d608SHuy Nguyen 
235e28408e9SHuy Nguyen #define MINIMUM_MAX_MTU 9216
2360696d608SHuy Nguyen int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv,
2370696d608SHuy Nguyen 				    u32 change, unsigned int mtu,
2380696d608SHuy Nguyen 				    struct ieee_pfc *pfc,
2390696d608SHuy Nguyen 				    u32 *buffer_size,
2400696d608SHuy Nguyen 				    u8 *prio2buffer)
2410696d608SHuy Nguyen {
2420696d608SHuy Nguyen 	struct mlx5e_port_buffer port_buffer;
2430696d608SHuy Nguyen 	u32 xoff = calculate_xoff(priv, mtu);
2440696d608SHuy Nguyen 	bool update_prio2buffer = false;
2450696d608SHuy Nguyen 	u8 buffer[MLX5E_MAX_PRIORITY];
2460696d608SHuy Nguyen 	bool update_buffer = false;
247e28408e9SHuy Nguyen 	unsigned int max_mtu;
2480696d608SHuy Nguyen 	u32 total_used = 0;
2490696d608SHuy Nguyen 	u8 curr_pfc_en;
2500696d608SHuy Nguyen 	int err;
2510696d608SHuy Nguyen 	int i;
2520696d608SHuy Nguyen 
2530696d608SHuy Nguyen 	mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change);
254e28408e9SHuy Nguyen 	max_mtu = max_t(unsigned int, priv->netdev->max_mtu, MINIMUM_MAX_MTU);
2550696d608SHuy Nguyen 
2560696d608SHuy Nguyen 	err = mlx5e_port_query_buffer(priv, &port_buffer);
2570696d608SHuy Nguyen 	if (err)
2580696d608SHuy Nguyen 		return err;
2590696d608SHuy Nguyen 
2600696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_CABLE_LEN) {
2610696d608SHuy Nguyen 		update_buffer = true;
262e28408e9SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, max_mtu);
2630696d608SHuy Nguyen 		if (err)
2640696d608SHuy Nguyen 			return err;
2650696d608SHuy Nguyen 	}
2660696d608SHuy Nguyen 
2670696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_PFC) {
2680696d608SHuy Nguyen 		err = mlx5e_port_query_priority2buffer(priv->mdev, buffer);
2690696d608SHuy Nguyen 		if (err)
2700696d608SHuy Nguyen 			return err;
2710696d608SHuy Nguyen 
272e28408e9SHuy Nguyen 		err = update_buffer_lossy(max_mtu, pfc->pfc_en, buffer, xoff,
2730696d608SHuy Nguyen 					  &port_buffer, &update_buffer);
2740696d608SHuy Nguyen 		if (err)
2750696d608SHuy Nguyen 			return err;
2760696d608SHuy Nguyen 	}
2770696d608SHuy Nguyen 
2780696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) {
2790696d608SHuy Nguyen 		update_prio2buffer = true;
2800696d608SHuy Nguyen 		err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL);
2810696d608SHuy Nguyen 		if (err)
2820696d608SHuy Nguyen 			return err;
2830696d608SHuy Nguyen 
284e28408e9SHuy Nguyen 		err = update_buffer_lossy(max_mtu, curr_pfc_en, prio2buffer,
285e28408e9SHuy Nguyen 					  xoff, &port_buffer, &update_buffer);
2860696d608SHuy Nguyen 		if (err)
2870696d608SHuy Nguyen 			return err;
2880696d608SHuy Nguyen 	}
2890696d608SHuy Nguyen 
2900696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_SIZE) {
2910696d608SHuy Nguyen 		for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
2920696d608SHuy Nguyen 			mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]);
2930696d608SHuy Nguyen 			if (!port_buffer.buffer[i].lossy && !buffer_size[i]) {
2940696d608SHuy Nguyen 				mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n",
2950696d608SHuy Nguyen 					  __func__, i);
2960696d608SHuy Nguyen 				return -EINVAL;
2970696d608SHuy Nguyen 			}
2980696d608SHuy Nguyen 
2990696d608SHuy Nguyen 			port_buffer.buffer[i].size = buffer_size[i];
3000696d608SHuy Nguyen 			total_used += buffer_size[i];
3010696d608SHuy Nguyen 		}
3020696d608SHuy Nguyen 
3030696d608SHuy Nguyen 		mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used);
3040696d608SHuy Nguyen 
3050696d608SHuy Nguyen 		if (total_used > port_buffer.port_buffer_size)
3060696d608SHuy Nguyen 			return -EINVAL;
3070696d608SHuy Nguyen 
3080696d608SHuy Nguyen 		update_buffer = true;
309e28408e9SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, max_mtu);
3100696d608SHuy Nguyen 		if (err)
3110696d608SHuy Nguyen 			return err;
3120696d608SHuy Nguyen 	}
3130696d608SHuy Nguyen 
3140696d608SHuy Nguyen 	/* Need to update buffer configuration if xoff value is changed */
3150696d608SHuy Nguyen 	if (!update_buffer && xoff != priv->dcbx.xoff) {
3160696d608SHuy Nguyen 		update_buffer = true;
317e28408e9SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, max_mtu);
3180696d608SHuy Nguyen 		if (err)
3190696d608SHuy Nguyen 			return err;
3200696d608SHuy Nguyen 	}
3210696d608SHuy Nguyen 	priv->dcbx.xoff = xoff;
3220696d608SHuy Nguyen 
3230696d608SHuy Nguyen 	/* Apply the settings */
3240696d608SHuy Nguyen 	if (update_buffer) {
3250696d608SHuy Nguyen 		err = port_set_buffer(priv, &port_buffer);
3260696d608SHuy Nguyen 		if (err)
3270696d608SHuy Nguyen 			return err;
3280696d608SHuy Nguyen 	}
3290696d608SHuy Nguyen 
3300696d608SHuy Nguyen 	if (update_prio2buffer)
3310696d608SHuy Nguyen 		err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer);
3320696d608SHuy Nguyen 
3330696d608SHuy Nguyen 	return err;
3340696d608SHuy Nguyen }
335