brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 459a536 Raw
236 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.5 */6 7#include "sparx5_main.h"8#include "sparx5_main_regs.h"9#include "sparx5_tc.h"10 11#define SPX5_MIRROR_PROBE_MAX 312#define SPX5_MIRROR_DISABLED 013#define SPX5_MIRROR_EGRESS 114#define SPX5_MIRROR_INGRESS 215#define SPX5_MIRROR_MONITOR_PORT_DEFAULT 6516#define SPX5_QFWD_MP_OFFSET 9 /* Mirror port offset in the QFWD register */17 18/* Convert from bool ingress/egress to mirror direction */19static u32 sparx5_mirror_to_dir(bool ingress)20{21	return ingress ? SPX5_MIRROR_INGRESS : SPX5_MIRROR_EGRESS;22}23 24/* Get ports belonging to this mirror */25static u64 sparx5_mirror_port_get(struct sparx5 *sparx5, u32 idx)26{27	return (u64)spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG1(idx)) << 32 |28	       spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG(idx));29}30 31/* Add port to mirror (only front ports) */32static void sparx5_mirror_port_add(struct sparx5 *sparx5, u32 idx, u32 portno)33{34	u64 reg = portno;35	u32 val;36 37	val = BIT(do_div(reg, 32));38 39	if (reg == 0)40		return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));41	else42		return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));43}44 45/* Delete port from mirror (only front ports) */46static void sparx5_mirror_port_del(struct sparx5 *sparx5, u32 idx, u32 portno)47{48	u64 reg = portno;49	u32 val;50 51	val = BIT(do_div(reg, 32));52 53	if (reg == 0)54		return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));55	else56		return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));57}58 59/* Check if mirror contains port */60static bool sparx5_mirror_contains(struct sparx5 *sparx5, u32 idx, u32 portno)61{62	return (sparx5_mirror_port_get(sparx5, idx) & BIT_ULL(portno)) != 0;63}64 65/* Check if mirror is empty */66static bool sparx5_mirror_is_empty(struct sparx5 *sparx5, u32 idx)67{68	return sparx5_mirror_port_get(sparx5, idx) == 0;69}70 71/* Get direction of mirror */72static u32 sparx5_mirror_dir_get(struct sparx5 *sparx5, u32 idx)73{74	u32 val = spx5_rd(sparx5, ANA_AC_PROBE_CFG(idx));75 76	return ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(val);77}78 79/* Set direction of mirror */80static void sparx5_mirror_dir_set(struct sparx5 *sparx5, u32 idx, u32 dir)81{82	spx5_rmw(ANA_AC_PROBE_CFG_PROBE_DIRECTION_SET(dir),83		 ANA_AC_PROBE_CFG_PROBE_DIRECTION, sparx5,84		 ANA_AC_PROBE_CFG(idx));85}86 87/* Set the monitor port for this mirror */88static void sparx5_mirror_monitor_set(struct sparx5 *sparx5, u32 idx,89				      u32 portno)90{91	spx5_rmw(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_SET(portno),92		 QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL, sparx5,93		 QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));94}95 96/* Get the monitor port of this mirror */97static u32 sparx5_mirror_monitor_get(struct sparx5 *sparx5, u32 idx)98{99	u32 val = spx5_rd(sparx5,100			  QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));101 102	return QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(val);103}104 105/* Check if port is the monitor port of this mirror */106static bool sparx5_mirror_has_monitor(struct sparx5 *sparx5, u32 idx,107				      u32 portno)108{109	return sparx5_mirror_monitor_get(sparx5, idx) == portno;110}111 112/* Get a suitable mirror for this port */113static int sparx5_mirror_get(struct sparx5_port *sport,114			     struct sparx5_port *mport, u32 dir, u32 *idx)115{116	struct sparx5 *sparx5 = sport->sparx5;117	u32 i;118 119	/* Check if this port is already used as a monitor port */120	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++)121		if (sparx5_mirror_has_monitor(sparx5, i, sport->portno))122			return -EINVAL;123 124	/* Check if existing mirror can be reused125	 * (same direction and monitor port).126	 */127	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {128		if (sparx5_mirror_dir_get(sparx5, i) == dir &&129		    sparx5_mirror_has_monitor(sparx5, i, mport->portno)) {130			*idx = i;131			return 0;132		}133	}134 135	/* Return free mirror */136	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {137		if (sparx5_mirror_is_empty(sparx5, i)) {138			*idx = i;139			return 0;140		}141	}142 143	return -ENOENT;144}145 146int sparx5_mirror_add(struct sparx5_mall_entry *entry)147{148	u32 mirror_idx, dir = sparx5_mirror_to_dir(entry->ingress);149	struct sparx5_port *sport, *mport;150	struct sparx5 *sparx5;151	int err;152 153	/* Source port */154	sport = entry->port;155	/* monitor port */156	mport = entry->mirror.port;157	sparx5 = sport->sparx5;158 159	if (sport->portno == mport->portno)160		return -EINVAL;161 162	err = sparx5_mirror_get(sport, mport, dir, &mirror_idx);163	if (err)164		return err;165 166	if (sparx5_mirror_contains(sparx5, mirror_idx, sport->portno))167		return -EEXIST;168 169	/* Add port to mirror */170	sparx5_mirror_port_add(sparx5, mirror_idx, sport->portno);171 172	/* Set direction of mirror */173	sparx5_mirror_dir_set(sparx5, mirror_idx, dir);174 175	/* Set monitor port for mirror */176	sparx5_mirror_monitor_set(sparx5, mirror_idx, mport->portno);177 178	entry->mirror.idx = mirror_idx;179 180	return 0;181}182 183void sparx5_mirror_del(struct sparx5_mall_entry *entry)184{185	struct sparx5_port *port = entry->port;186	struct sparx5 *sparx5 = port->sparx5;187	u32 mirror_idx = entry->mirror.idx;188 189	sparx5_mirror_port_del(sparx5, mirror_idx, port->portno);190	if (!sparx5_mirror_is_empty(sparx5, mirror_idx))191		return;192 193	sparx5_mirror_dir_set(sparx5, mirror_idx, SPX5_MIRROR_DISABLED);194 195	sparx5_mirror_monitor_set(sparx5,196				  mirror_idx,197				  SPX5_MIRROR_MONITOR_PORT_DEFAULT);198}199 200void sparx5_mirror_stats(struct sparx5_mall_entry *entry,201			 struct flow_stats *fstats)202{203	struct sparx5_port *port = entry->port;204	struct rtnl_link_stats64 new_stats;205	struct flow_stats *old_stats;206 207	old_stats = &entry->port->mirror_stats;208	sparx5_get_stats64(port->ndev, &new_stats);209 210	if (entry->ingress) {211		flow_stats_update(fstats,212				  new_stats.rx_bytes - old_stats->bytes,213				  new_stats.rx_packets - old_stats->pkts,214				  new_stats.rx_dropped - old_stats->drops,215				  old_stats->lastused,216				  FLOW_ACTION_HW_STATS_IMMEDIATE);217 218		old_stats->bytes = new_stats.rx_bytes;219		old_stats->pkts = new_stats.rx_packets;220		old_stats->drops = new_stats.rx_dropped;221		old_stats->lastused = jiffies;222	} else {223		flow_stats_update(fstats,224				  new_stats.tx_bytes - old_stats->bytes,225				  new_stats.tx_packets - old_stats->pkts,226				  new_stats.tx_dropped - old_stats->drops,227				  old_stats->lastused,228				  FLOW_ACTION_HW_STATS_IMMEDIATE);229 230		old_stats->bytes = new_stats.tx_bytes;231		old_stats->pkts = new_stats.tx_packets;232		old_stats->drops = new_stats.tx_dropped;233		old_stats->lastused = jiffies;234	}235}236