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 
1250696d608SHuy Nguyen /* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) */
1260696d608SHuy Nguyen static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu)
1270696d608SHuy Nguyen {
1280696d608SHuy Nguyen 	u32 speed;
1290696d608SHuy Nguyen 	u32 xoff;
1300696d608SHuy Nguyen 	int err;
1310696d608SHuy Nguyen 
1320696d608SHuy Nguyen 	err = mlx5e_port_linkspeed(priv->mdev, &speed);
13364e28334SShay Agroskin 	if (err) {
13464e28334SShay Agroskin 		mlx5_core_warn(priv->mdev, "cannot get port speed\n");
1350696d608SHuy Nguyen 		return 0;
13664e28334SShay Agroskin 	}
1370696d608SHuy Nguyen 
1380696d608SHuy Nguyen 	xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100;
1390696d608SHuy Nguyen 
1400696d608SHuy Nguyen 	mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff);
1410696d608SHuy Nguyen 	return xoff;
1420696d608SHuy Nguyen }
1430696d608SHuy Nguyen 
1440696d608SHuy Nguyen static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer,
1450696d608SHuy Nguyen 				 u32 xoff, unsigned int mtu)
1460696d608SHuy Nguyen {
1470696d608SHuy Nguyen 	int i;
1480696d608SHuy Nguyen 
1490696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
1500696d608SHuy Nguyen 		if (port_buffer->buffer[i].lossy) {
1510696d608SHuy Nguyen 			port_buffer->buffer[i].xoff = 0;
1520696d608SHuy Nguyen 			port_buffer->buffer[i].xon  = 0;
1530696d608SHuy Nguyen 			continue;
1540696d608SHuy Nguyen 		}
1550696d608SHuy Nguyen 
1560696d608SHuy Nguyen 		if (port_buffer->buffer[i].size <
1570696d608SHuy Nguyen 		    (xoff + mtu + (1 << MLX5E_BUFFER_CELL_SHIFT)))
1580696d608SHuy Nguyen 			return -ENOMEM;
1590696d608SHuy Nguyen 
1600696d608SHuy Nguyen 		port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff;
1610696d608SHuy Nguyen 		port_buffer->buffer[i].xon  = port_buffer->buffer[i].xoff - mtu;
1620696d608SHuy Nguyen 	}
1630696d608SHuy Nguyen 
1640696d608SHuy Nguyen 	return 0;
1650696d608SHuy Nguyen }
1660696d608SHuy Nguyen 
1670696d608SHuy Nguyen /**
168d3669ca9SSaeed Mahameed  *	update_buffer_lossy	- Update buffer configuration based on pfc
169d3669ca9SSaeed Mahameed  *	@mtu: device's MTU
170d3669ca9SSaeed Mahameed  *	@pfc_en: <input> current pfc configuration
171d3669ca9SSaeed Mahameed  *	@buffer: <input> current prio to buffer mapping
172d3669ca9SSaeed Mahameed  *	@xoff:   <input> xoff value
173d3669ca9SSaeed Mahameed  *	@port_buffer: <output> port receive buffer configuration
174d3669ca9SSaeed Mahameed  *	@change: <output>
1750696d608SHuy Nguyen  *
176d3669ca9SSaeed Mahameed  *	Update buffer configuration based on pfc configuraiton and
177d3669ca9SSaeed Mahameed  *	priority to buffer mapping.
1780696d608SHuy Nguyen  *	Buffer's lossy bit is changed to:
179d3669ca9SSaeed Mahameed  *		lossless if there is at least one PFC enabled priority
180d3669ca9SSaeed Mahameed  *		mapped to this buffer lossy if all priorities mapped to
181d3669ca9SSaeed Mahameed  *		this buffer are PFC disabled
1820696d608SHuy Nguyen  *
183d3669ca9SSaeed Mahameed  *	@return: 0 if no error,
184d3669ca9SSaeed Mahameed  *	sets change to true if buffer configuration was modified.
1850696d608SHuy Nguyen  */
1860696d608SHuy Nguyen static int update_buffer_lossy(unsigned int mtu,
1870696d608SHuy Nguyen 			       u8 pfc_en, u8 *buffer, u32 xoff,
1880696d608SHuy Nguyen 			       struct mlx5e_port_buffer *port_buffer,
1890696d608SHuy Nguyen 			       bool *change)
1900696d608SHuy Nguyen {
1910696d608SHuy Nguyen 	bool changed = false;
1920696d608SHuy Nguyen 	u8 lossy_count;
1930696d608SHuy Nguyen 	u8 prio_count;
1940696d608SHuy Nguyen 	u8 lossy;
1950696d608SHuy Nguyen 	int prio;
1960696d608SHuy Nguyen 	int err;
1970696d608SHuy Nguyen 	int i;
1980696d608SHuy Nguyen 
1990696d608SHuy Nguyen 	for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
2000696d608SHuy Nguyen 		prio_count = 0;
2010696d608SHuy Nguyen 		lossy_count = 0;
2020696d608SHuy Nguyen 
2030696d608SHuy Nguyen 		for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) {
2040696d608SHuy Nguyen 			if (buffer[prio] != i)
2050696d608SHuy Nguyen 				continue;
2060696d608SHuy Nguyen 
2070696d608SHuy Nguyen 			prio_count++;
2080696d608SHuy Nguyen 			lossy_count += !(pfc_en & (1 << prio));
2090696d608SHuy Nguyen 		}
2100696d608SHuy Nguyen 
2110696d608SHuy Nguyen 		if (lossy_count == prio_count)
2120696d608SHuy Nguyen 			lossy = 1;
2130696d608SHuy Nguyen 		else /* lossy_count < prio_count */
2140696d608SHuy Nguyen 			lossy = 0;
2150696d608SHuy Nguyen 
2160696d608SHuy Nguyen 		if (lossy != port_buffer->buffer[i].lossy) {
2170696d608SHuy Nguyen 			port_buffer->buffer[i].lossy = lossy;
2180696d608SHuy Nguyen 			changed = true;
2190696d608SHuy Nguyen 		}
2200696d608SHuy Nguyen 	}
2210696d608SHuy Nguyen 
2220696d608SHuy Nguyen 	if (changed) {
2230696d608SHuy Nguyen 		err = update_xoff_threshold(port_buffer, xoff, mtu);
2240696d608SHuy Nguyen 		if (err)
2250696d608SHuy Nguyen 			return err;
2260696d608SHuy Nguyen 
2270696d608SHuy Nguyen 		*change = true;
2280696d608SHuy Nguyen 	}
2290696d608SHuy Nguyen 
2300696d608SHuy Nguyen 	return 0;
2310696d608SHuy Nguyen }
2320696d608SHuy Nguyen 
2330696d608SHuy Nguyen int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv,
2340696d608SHuy Nguyen 				    u32 change, unsigned int mtu,
2350696d608SHuy Nguyen 				    struct ieee_pfc *pfc,
2360696d608SHuy Nguyen 				    u32 *buffer_size,
2370696d608SHuy Nguyen 				    u8 *prio2buffer)
2380696d608SHuy Nguyen {
2390696d608SHuy Nguyen 	struct mlx5e_port_buffer port_buffer;
2400696d608SHuy Nguyen 	u32 xoff = calculate_xoff(priv, mtu);
2410696d608SHuy Nguyen 	bool update_prio2buffer = false;
2420696d608SHuy Nguyen 	u8 buffer[MLX5E_MAX_PRIORITY];
2430696d608SHuy Nguyen 	bool update_buffer = false;
2440696d608SHuy Nguyen 	u32 total_used = 0;
2450696d608SHuy Nguyen 	u8 curr_pfc_en;
2460696d608SHuy Nguyen 	int err;
2470696d608SHuy Nguyen 	int i;
2480696d608SHuy Nguyen 
2490696d608SHuy Nguyen 	mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change);
2500696d608SHuy Nguyen 
2510696d608SHuy Nguyen 	err = mlx5e_port_query_buffer(priv, &port_buffer);
2520696d608SHuy Nguyen 	if (err)
2530696d608SHuy Nguyen 		return err;
2540696d608SHuy Nguyen 
2550696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_CABLE_LEN) {
2560696d608SHuy Nguyen 		update_buffer = true;
2570696d608SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, mtu);
2580696d608SHuy Nguyen 		if (err)
2590696d608SHuy Nguyen 			return err;
2600696d608SHuy Nguyen 	}
2610696d608SHuy Nguyen 
2620696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_PFC) {
2630696d608SHuy Nguyen 		err = mlx5e_port_query_priority2buffer(priv->mdev, buffer);
2640696d608SHuy Nguyen 		if (err)
2650696d608SHuy Nguyen 			return err;
2660696d608SHuy Nguyen 
2670696d608SHuy Nguyen 		err = update_buffer_lossy(mtu, pfc->pfc_en, buffer, xoff,
2680696d608SHuy Nguyen 					  &port_buffer, &update_buffer);
2690696d608SHuy Nguyen 		if (err)
2700696d608SHuy Nguyen 			return err;
2710696d608SHuy Nguyen 	}
2720696d608SHuy Nguyen 
2730696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) {
2740696d608SHuy Nguyen 		update_prio2buffer = true;
2750696d608SHuy Nguyen 		err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL);
2760696d608SHuy Nguyen 		if (err)
2770696d608SHuy Nguyen 			return err;
2780696d608SHuy Nguyen 
2790696d608SHuy Nguyen 		err = update_buffer_lossy(mtu, curr_pfc_en, prio2buffer, xoff,
2800696d608SHuy Nguyen 					  &port_buffer, &update_buffer);
2810696d608SHuy Nguyen 		if (err)
2820696d608SHuy Nguyen 			return err;
2830696d608SHuy Nguyen 	}
2840696d608SHuy Nguyen 
2850696d608SHuy Nguyen 	if (change & MLX5E_PORT_BUFFER_SIZE) {
2860696d608SHuy Nguyen 		for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
2870696d608SHuy Nguyen 			mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]);
2880696d608SHuy Nguyen 			if (!port_buffer.buffer[i].lossy && !buffer_size[i]) {
2890696d608SHuy Nguyen 				mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n",
2900696d608SHuy Nguyen 					  __func__, i);
2910696d608SHuy Nguyen 				return -EINVAL;
2920696d608SHuy Nguyen 			}
2930696d608SHuy Nguyen 
2940696d608SHuy Nguyen 			port_buffer.buffer[i].size = buffer_size[i];
2950696d608SHuy Nguyen 			total_used += buffer_size[i];
2960696d608SHuy Nguyen 		}
2970696d608SHuy Nguyen 
2980696d608SHuy Nguyen 		mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used);
2990696d608SHuy Nguyen 
3000696d608SHuy Nguyen 		if (total_used > port_buffer.port_buffer_size)
3010696d608SHuy Nguyen 			return -EINVAL;
3020696d608SHuy Nguyen 
3030696d608SHuy Nguyen 		update_buffer = true;
3040696d608SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, mtu);
3050696d608SHuy Nguyen 		if (err)
3060696d608SHuy Nguyen 			return err;
3070696d608SHuy Nguyen 	}
3080696d608SHuy Nguyen 
3090696d608SHuy Nguyen 	/* Need to update buffer configuration if xoff value is changed */
3100696d608SHuy Nguyen 	if (!update_buffer && xoff != priv->dcbx.xoff) {
3110696d608SHuy Nguyen 		update_buffer = true;
3120696d608SHuy Nguyen 		err = update_xoff_threshold(&port_buffer, xoff, mtu);
3130696d608SHuy Nguyen 		if (err)
3140696d608SHuy Nguyen 			return err;
3150696d608SHuy Nguyen 	}
3160696d608SHuy Nguyen 	priv->dcbx.xoff = xoff;
3170696d608SHuy Nguyen 
3180696d608SHuy Nguyen 	/* Apply the settings */
3190696d608SHuy Nguyen 	if (update_buffer) {
3200696d608SHuy Nguyen 		err = port_set_buffer(priv, &port_buffer);
3210696d608SHuy Nguyen 		if (err)
3220696d608SHuy Nguyen 			return err;
3230696d608SHuy Nguyen 	}
3240696d608SHuy Nguyen 
3250696d608SHuy Nguyen 	if (update_prio2buffer)
3260696d608SHuy Nguyen 		err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer);
3270696d608SHuy Nguyen 
3280696d608SHuy Nguyen 	return err;
3290696d608SHuy Nguyen }
330