linux/drivers/net/ethernet/mscc/ocelot.c

// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
 * Microsemi Ocelot Switch driver
 *
 * Copyright (c) 2017 Microsemi Corporation
 */
#include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h>
#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <net/pkt_sched.h>
#include <soc/mscc/ocelot_hsio.h>
#include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h"
#include "ocelot_vcap.h"

#define TABLE_UPDATE_SLEEP_US
#define TABLE_UPDATE_TIMEOUT_US
#define MEM_INIT_SLEEP_US
#define MEM_INIT_TIMEOUT_US

#define OCELOT_RSV_VLAN_RANGE_START

struct ocelot_mact_entry {};

/* Caller must hold &ocelot->mact_lock */
static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
{}

/* Caller must hold &ocelot->mact_lock */
static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
{}

/* Caller must hold &ocelot->mact_lock */
static void ocelot_mact_select(struct ocelot *ocelot,
			       const unsigned char mac[ETH_ALEN],
			       unsigned int vid)
{}

static int __ocelot_mact_learn(struct ocelot *ocelot, int port,
			       const unsigned char mac[ETH_ALEN],
			       unsigned int vid, enum macaccess_entry_type type)
{}

int ocelot_mact_learn(struct ocelot *ocelot, int port,
		      const unsigned char mac[ETH_ALEN],
		      unsigned int vid, enum macaccess_entry_type type)
{}
EXPORT_SYMBOL();

int ocelot_mact_forget(struct ocelot *ocelot,
		       const unsigned char mac[ETH_ALEN], unsigned int vid)
{}
EXPORT_SYMBOL();

int ocelot_mact_lookup(struct ocelot *ocelot, int *dst_idx,
		       const unsigned char mac[ETH_ALEN],
		       unsigned int vid, enum macaccess_entry_type *type)
{}
EXPORT_SYMBOL();

int ocelot_mact_learn_streamdata(struct ocelot *ocelot, int dst_idx,
				 const unsigned char mac[ETH_ALEN],
				 unsigned int vid,
				 enum macaccess_entry_type type,
				 int sfid, int ssid)
{}
EXPORT_SYMBOL();

static void ocelot_mact_init(struct ocelot *ocelot)
{}

void ocelot_pll5_init(struct ocelot *ocelot)
{}
EXPORT_SYMBOL();

static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
{}

static int ocelot_single_vlan_aware_bridge(struct ocelot *ocelot,
					   struct netlink_ext_ack *extack)
{}

static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
{}

static inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
{}

static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
{}

static int ocelot_port_num_untagged_vlans(struct ocelot *ocelot, int port)
{}

static int ocelot_port_num_tagged_vlans(struct ocelot *ocelot, int port)
{}

/* We use native VLAN when we have to mix egress-tagged VLANs with exactly
 * _one_ egress-untagged VLAN (_the_ native VLAN)
 */
static bool ocelot_port_uses_native_vlan(struct ocelot *ocelot, int port)
{}

static struct ocelot_bridge_vlan *
ocelot_port_find_native_vlan(struct ocelot *ocelot, int port)
{}

/* Keep in sync REW_TAG_CFG_TAG_CFG and, if applicable,
 * REW_PORT_VLAN_CFG_PORT_VID, with the bridge VLAN table and VLAN awareness
 * state of the port.
 */
static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port)
{}

int ocelot_bridge_num_find(struct ocelot *ocelot,
			   const struct net_device *bridge)
{}
EXPORT_SYMBOL_GPL();

static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
				    const struct net_device *bridge)
{}

/**
 * ocelot_update_vlan_reclassify_rule() - Make switch aware only to bridge VLAN TPID
 *
 * @ocelot: Switch private data structure
 * @port: Index of ingress port
 *
 * IEEE 802.1Q-2018 clauses "5.5 C-VLAN component conformance" and "5.6 S-VLAN
 * component conformance" suggest that a C-VLAN component should only recognize
 * and filter on C-Tags, and an S-VLAN component should only recognize and
 * process based on C-Tags.
 *
 * In Linux, as per commit 1a0b20b25732 ("Merge branch 'bridge-next'"), C-VLAN
 * components are largely represented by a bridge with vlan_protocol 802.1Q,
 * and S-VLAN components by a bridge with vlan_protocol 802.1ad.
 *
 * Currently the driver only offloads vlan_protocol 802.1Q, but the hardware
 * design is non-conformant, because the switch assigns each frame to a VLAN
 * based on an entirely different question, as detailed in figure "Basic VLAN
 * Classification Flow" from its manual and reproduced below.
 *
 * Set TAG_TYPE, PCP, DEI, VID to port-default values in VLAN_CFG register
 * if VLAN_AWARE_ENA[port] and frame has outer tag then:
 *   if VLAN_INNER_TAG_ENA[port] and frame has inner tag then:
 *     TAG_TYPE = (Frame.InnerTPID <> 0x8100)
 *     Set PCP, DEI, VID to values from inner VLAN header
 *   else:
 *     TAG_TYPE = (Frame.OuterTPID <> 0x8100)
 *     Set PCP, DEI, VID to values from outer VLAN header
 *   if VID == 0 then:
 *     VID = VLAN_CFG.VLAN_VID
 *
 * Summarized, the switch will recognize both 802.1Q and 802.1ad TPIDs as VLAN
 * "with equal rights", and just set the TAG_TYPE bit to 0 (if 802.1Q) or to 1
 * (if 802.1ad). It will classify based on whichever of the tags is "outer", no
 * matter what TPID that may have (or "inner", if VLAN_INNER_TAG_ENA[port]).
 *
 * In the VLAN Table, the TAG_TYPE information is not accessible - just the
 * classified VID is - so it is as if each VLAN Table entry is for 2 VLANs:
 * C-VLAN X, and S-VLAN X.
 *
 * Whereas the Linux bridge behavior is to only filter on frames with a TPID
 * equal to the vlan_protocol, and treat everything else as VLAN-untagged.
 *
 * Consider an ingress packet tagged with 802.1ad VID=3 and 802.1Q VID=5,
 * received on a bridge vlan_filtering=1 vlan_protocol=802.1Q port. This frame
 * should be treated as 802.1Q-untagged, and classified to the PVID of that
 * bridge port. Not to VID=3, and not to VID=5.
 *
 * The VCAP IS1 TCAM has everything we need to overwrite the choices made in
 * the basic VLAN classification pipeline: it can match on TAG_TYPE in the key,
 * and it can modify the classified VID in the action. Thus, for each port
 * under a vlan_filtering bridge, we can insert a rule in VCAP IS1 lookup 0 to
 * match on 802.1ad tagged frames and modify their classified VID to the 802.1Q
 * PVID of the port. This effectively makes it appear to the outside world as
 * if those packets were processed as VLAN-untagged.
 *
 * The rule needs to be updated each time the bridge PVID changes, and needs
 * to be deleted if the bridge PVID is deleted, or if the port becomes
 * VLAN-unaware.
 */
static int ocelot_update_vlan_reclassify_rule(struct ocelot *ocelot, int port)
{}

/* Default vlan to clasify for untagged frames (may be zero) */
static int ocelot_port_set_pvid(struct ocelot *ocelot, int port,
				const struct ocelot_bridge_vlan *pvid_vlan)
{}

static struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
							  u16 vid)
{}

static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid,
				  bool untagged)
{}

static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid)
{}

static int ocelot_add_vlan_unaware_pvid(struct ocelot *ocelot, int port,
					const struct net_device *bridge)
{}

static int ocelot_del_vlan_unaware_pvid(struct ocelot *ocelot, int port,
					const struct net_device *bridge)
{}

int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
			       bool vlan_aware, struct netlink_ext_ack *extack)
{}
EXPORT_SYMBOL();

int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
			bool untagged, struct netlink_ext_ack *extack)
{}
EXPORT_SYMBOL();

int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
		    bool untagged)
{}
EXPORT_SYMBOL();

int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
{}
EXPORT_SYMBOL();

static void ocelot_vlan_init(struct ocelot *ocelot)
{}

static u32 ocelot_read_eq_avail(struct ocelot *ocelot, int port)
{}

static int ocelot_port_flush(struct ocelot *ocelot, int port)
{}

int ocelot_port_configure_serdes(struct ocelot *ocelot, int port,
				 struct device_node *portnp)
{}
EXPORT_SYMBOL_GPL();

void ocelot_phylink_mac_config(struct ocelot *ocelot, int port,
			       unsigned int link_an_mode,
			       const struct phylink_link_state *state)
{}
EXPORT_SYMBOL_GPL();

void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
				  unsigned int link_an_mode,
				  phy_interface_t interface,
				  unsigned long quirks)
{}
EXPORT_SYMBOL_GPL();

void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
				struct phy_device *phydev,
				unsigned int link_an_mode,
				phy_interface_t interface,
				int speed, int duplex,
				bool tx_pause, bool rx_pause,
				unsigned long quirks)
{}
EXPORT_SYMBOL_GPL();

static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
				u32 *rval)
{}

static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
{}

void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
			     u64 timestamp)
{}
EXPORT_SYMBOL();

void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp)
			 __acquires(&ocelot->inj_lock)
{}
EXPORT_SYMBOL_GPL();

void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp)
			   __releases(&ocelot->inj_lock)
{}
EXPORT_SYMBOL_GPL();

void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp)
			 __acquires(&ocelot->inj_lock)
{}
EXPORT_SYMBOL_GPL();

void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp)
			   __releases(&ocelot->inj_lock)
{}
EXPORT_SYMBOL_GPL();

void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp)
			    __acquires(&ocelot->xtr_lock)
{}
EXPORT_SYMBOL_GPL();

void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp)
			      __releases(&ocelot->xtr_lock)
{}
EXPORT_SYMBOL_GPL();

int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{}
EXPORT_SYMBOL();

bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{}
EXPORT_SYMBOL();

/**
 * ocelot_ifh_set_basic - Set basic information in Injection Frame Header
 * @ifh: Pointer to Injection Frame Header memory
 * @ocelot: Switch private data structure
 * @port: Egress port number
 * @rew_op: Egress rewriter operation for PTP
 * @skb: Pointer to socket buffer (packet)
 *
 * Populate the Injection Frame Header with basic information for this skb: the
 * analyzer bypass bit, destination port, VLAN info, egress rewriter info.
 */
void ocelot_ifh_set_basic(void *ifh, struct ocelot *ocelot, int port,
			  u32 rew_op, struct sk_buff *skb)
{}
EXPORT_SYMBOL();

void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
			      u32 rew_op, struct sk_buff *skb)
{}
EXPORT_SYMBOL();

void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{}
EXPORT_SYMBOL();

int ocelot_fdb_add(struct ocelot *ocelot, int port, const unsigned char *addr,
		   u16 vid, const struct net_device *bridge)
{}
EXPORT_SYMBOL();

int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
		   u16 vid, const struct net_device *bridge)
{}
EXPORT_SYMBOL();

/* Caller must hold &ocelot->mact_lock */
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
			    struct ocelot_mact_entry *entry)
{}

int ocelot_mact_flush(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL_GPL();

int ocelot_fdb_dump(struct ocelot *ocelot, int port,
		    dsa_fdb_dump_cb_t *cb, void *data)
{}
EXPORT_SYMBOL();

int ocelot_trap_add(struct ocelot *ocelot, int port,
		    unsigned long cookie, bool take_ts,
		    void (*populate)(struct ocelot_vcap_filter *f))
{}

int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie)
{}

static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond)
{}

/* The logical port number of a LAG is equal to the lowest numbered physical
 * port ID present in that LAG. It may change if that port ever leaves the LAG.
 */
int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond)
{}
EXPORT_SYMBOL_GPL();

/* Returns the mask of user ports assigned to this DSA tag_8021q CPU port.
 * Note that when CPU ports are in a LAG, the user ports are assigned to the
 * 'primary' CPU port, the one whose physical port number gives the logical
 * port number of the LAG.
 *
 * We leave PGID_SRC poorly configured for the 'secondary' CPU port in the LAG
 * (to which no user port is assigned), but it appears that forwarding from
 * this secondary CPU port looks at the PGID_SRC associated with the logical
 * port ID that it's assigned to, which *is* configured properly.
 */
static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot,
					       struct ocelot_port *cpu)
{}

/* Returns the DSA tag_8021q CPU port that the given port is assigned to,
 * or the bit mask of CPU ports if said CPU port is in a LAG.
 */
u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL_GPL();

u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port)
{}
EXPORT_SYMBOL_GPL();

static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
{}

/* Update PGID_CPU which is the destination port mask used for whitelisting
 * unicast addresses filtered towards the host. In the normal and NPI modes,
 * this points to the analyzer entry for the CPU port module, while in DSA
 * tag_8021q mode, it is a bit mask of all active CPU ports.
 * PGID_SRC will take care of forwarding a packet from one user port to
 * no more than a single CPU port.
 */
static void ocelot_update_pgid_cpu(struct ocelot *ocelot)
{}

void ocelot_port_setup_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
{}
EXPORT_SYMBOL_GPL();

void ocelot_port_teardown_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
{}
EXPORT_SYMBOL_GPL();

void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port,
				      int cpu)
{}
EXPORT_SYMBOL_GPL();

void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL_GPL();

void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{}
EXPORT_SYMBOL();

void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
{}
EXPORT_SYMBOL();

static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
						     const unsigned char *addr,
						     u16 vid)
{}

static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
{}

static struct ocelot_pgid *ocelot_pgid_alloc(struct ocelot *ocelot, int index,
					     unsigned long ports)
{}

static void ocelot_pgid_free(struct ocelot *ocelot, struct ocelot_pgid *pgid)
{}

static struct ocelot_pgid *ocelot_mdb_get_pgid(struct ocelot *ocelot,
					       const struct ocelot_multicast *mc)
{}

static void ocelot_encode_ports_to_mdb(unsigned char *addr,
				       struct ocelot_multicast *mc)
{}

int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
			const struct switchdev_obj_port_mdb *mdb,
			const struct net_device *bridge)
{}
EXPORT_SYMBOL();

int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
			const struct switchdev_obj_port_mdb *mdb,
			const struct net_device *bridge)
{}
EXPORT_SYMBOL();

int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
			    struct net_device *bridge, int bridge_num,
			    struct netlink_ext_ack *extack)
{}
EXPORT_SYMBOL();

void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
			      struct net_device *bridge)
{}
EXPORT_SYMBOL();

static void ocelot_set_aggr_pgids(struct ocelot *ocelot)
{}

/* When offloading a bonding interface, the switch ports configured under the
 * same bond must have the same logical port ID, equal to the physical port ID
 * of the lowest numbered physical port in that bond. Otherwise, in standalone/
 * bridged mode, each port has a logical port ID equal to its physical port ID.
 */
static void ocelot_setup_logical_port_ids(struct ocelot *ocelot)
{}

static int ocelot_migrate_mc(struct ocelot *ocelot, struct ocelot_multicast *mc,
			     unsigned long from_mask, unsigned long to_mask)
{}

int ocelot_migrate_mdbs(struct ocelot *ocelot, unsigned long from_mask,
			unsigned long to_mask)
{}
EXPORT_SYMBOL_GPL();

/* Documentation for PORTID_VAL says:
 *     Logical port number for front port. If port is not a member of a LLAG,
 *     then PORTID must be set to the physical port number.
 *     If port is a member of a LLAG, then PORTID must be set to the common
 *     PORTID_VAL used for all member ports of the LLAG.
 *     The value must not exceed the number of physical ports on the device.
 *
 * This means we have little choice but to migrate FDB entries pointing towards
 * a logical port when that changes.
 */
static void ocelot_migrate_lag_fdbs(struct ocelot *ocelot,
				    struct net_device *bond,
				    int lag)
{}

int ocelot_port_lag_join(struct ocelot *ocelot, int port,
			 struct net_device *bond,
			 struct netdev_lag_upper_info *info,
			 struct netlink_ext_ack *extack)
{}
EXPORT_SYMBOL();

void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
			   struct net_device *bond)
{}
EXPORT_SYMBOL();

void ocelot_port_lag_change(struct ocelot *ocelot, int port, bool lag_tx_active)
{}
EXPORT_SYMBOL();

int ocelot_lag_fdb_add(struct ocelot *ocelot, struct net_device *bond,
		       const unsigned char *addr, u16 vid,
		       const struct net_device *bridge)
{}
EXPORT_SYMBOL_GPL();

int ocelot_lag_fdb_del(struct ocelot *ocelot, struct net_device *bond,
		       const unsigned char *addr, u16 vid,
		       const struct net_device *bridge)
{}
EXPORT_SYMBOL_GPL();

/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
 * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
 * In the special case that it's the NPI port that we're configuring, the
 * length of the tag and optional prefix needs to be accounted for privately,
 * in order to be able to sustain communication at the requested @sdu.
 */
void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu)
{}
EXPORT_SYMBOL();

int ocelot_get_max_mtu(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL();

static void ocelot_port_set_learning(struct ocelot *ocelot, int port,
				     bool enabled)
{}

static void ocelot_port_set_ucast_flood(struct ocelot *ocelot, int port,
					bool enabled)
{}

static void ocelot_port_set_mcast_flood(struct ocelot *ocelot, int port,
					bool enabled)
{}

static void ocelot_port_set_bcast_flood(struct ocelot *ocelot, int port,
					bool enabled)
{}

int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
				 struct switchdev_brport_flags flags)
{}
EXPORT_SYMBOL();

void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
			      struct switchdev_brport_flags flags)
{}
EXPORT_SYMBOL();

int ocelot_port_get_default_prio(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL_GPL();

int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
{}
EXPORT_SYMBOL_GPL();

int ocelot_port_get_dscp_prio(struct ocelot *ocelot, int port, u8 dscp)
{}
EXPORT_SYMBOL_GPL();

int ocelot_port_add_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
{}
EXPORT_SYMBOL_GPL();

int ocelot_port_del_dscp_prio(struct ocelot *ocelot, int port, u8 dscp, u8 prio)
{}
EXPORT_SYMBOL_GPL();

struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to,
					struct netlink_ext_ack *extack)
{}

void ocelot_mirror_put(struct ocelot *ocelot)
{}

int ocelot_port_mirror_add(struct ocelot *ocelot, int from, int to,
			   bool ingress, struct netlink_ext_ack *extack)
{}
EXPORT_SYMBOL_GPL();

void ocelot_port_mirror_del(struct ocelot *ocelot, int from, bool ingress)
{}
EXPORT_SYMBOL_GPL();

static void ocelot_port_reset_mqprio(struct ocelot *ocelot, int port)
{}

int ocelot_port_mqprio(struct ocelot *ocelot, int port,
		       struct tc_mqprio_qopt_offload *mqprio)
{}
EXPORT_SYMBOL_GPL();

void ocelot_init_port(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL();

/* Configure and enable the CPU port module, which is a set of queues
 * accessible through register MMIO, frame DMA or Ethernet (in case
 * NPI mode is used).
 */
static void ocelot_cpu_port_init(struct ocelot *ocelot)
{}

static void ocelot_detect_features(struct ocelot *ocelot)
{}

static int ocelot_mem_init_status(struct ocelot *ocelot)
{}

int ocelot_reset(struct ocelot *ocelot)
{}
EXPORT_SYMBOL();

int ocelot_init(struct ocelot *ocelot)
{}
EXPORT_SYMBOL();

void ocelot_deinit(struct ocelot *ocelot)
{}
EXPORT_SYMBOL();

void ocelot_deinit_port(struct ocelot *ocelot, int port)
{}
EXPORT_SYMBOL();

MODULE_DESCRIPTION();
MODULE_LICENSE();