// SPDX-License-Identifier: GPL-2.0
/*
* Lantiq / Intel GSWIP switch driver for VRX200 , xRX300 and xRX330 SoCs
*
* Copyright ( C ) 2010 Lantiq Deutschland
* Copyright ( C ) 2012 John Crispin < john @ phrozen . org >
* Copyright ( C ) 2017 - 2019 Hauke Mehrtens < hauke @ hauke - m . de >
*
* The VLAN and bridge model the GSWIP hardware uses does not directly
* matches the model DSA uses .
*
* The hardware has 64 possible table entries for bridges with one VLAN
* ID , one flow id and a list of ports for each bridge . All entries which
* match the same flow ID are combined in the mac learning table , they
* act as one global bridge .
* The hardware does not support VLAN filter on the port , but on the
* bridge , this driver converts the DSA model to the hardware .
*
* The CPU gets all the exception frames which do not match any forwarding
* rule and the CPU port is also added to all bridges . This makes it possible
* to handle all the special cases easily in software .
* At the initialization the driver allocates one bridge table entry for
* each switch port which is used when the port is used without an
* explicit bridge . This prevents the frames from being forwarded
* between all LAN ports by default .
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <net/dsa.h>
#include <dt-bindings/mips/lantiq_rcu_gphy.h>
#include "lantiq_pce.h"
/* GSWIP MDIO Registers */
#define GSWIP_MDIO_GLOB 0 x00
#define GSWIP_MDIO_GLOB_ENABLE BIT(15 )
#define GSWIP_MDIO_CTRL 0 x08
#define GSWIP_MDIO_CTRL_BUSY BIT(12 )
#define GSWIP_MDIO_CTRL_RD BIT(11 )
#define GSWIP_MDIO_CTRL_WR BIT(10 )
#define GSWIP_MDIO_CTRL_PHYAD_MASK 0 x1f
#define GSWIP_MDIO_CTRL_PHYAD_SHIFT 5
#define GSWIP_MDIO_CTRL_REGAD_MASK 0 x1f
#define GSWIP_MDIO_READ 0 x09
#define GSWIP_MDIO_WRITE 0 x0A
#define GSWIP_MDIO_MDC_CFG0 0 x0B
#define GSWIP_MDIO_MDC_CFG1 0 x0C
#define GSWIP_MDIO_PHYp(p) (0 x15 - (p))
#define GSWIP_MDIO_PHY_LINK_MASK 0 x6000
#define GSWIP_MDIO_PHY_LINK_AUTO 0 x0000
#define GSWIP_MDIO_PHY_LINK_DOWN 0 x4000
#define GSWIP_MDIO_PHY_LINK_UP 0 x2000
#define GSWIP_MDIO_PHY_SPEED_MASK 0 x1800
#define GSWIP_MDIO_PHY_SPEED_AUTO 0 x1800
#define GSWIP_MDIO_PHY_SPEED_M10 0 x0000
#define GSWIP_MDIO_PHY_SPEED_M100 0 x0800
#define GSWIP_MDIO_PHY_SPEED_G1 0 x1000
#define GSWIP_MDIO_PHY_FDUP_MASK 0 x0600
#define GSWIP_MDIO_PHY_FDUP_AUTO 0 x0000
#define GSWIP_MDIO_PHY_FDUP_EN 0 x0200
#define GSWIP_MDIO_PHY_FDUP_DIS 0 x0600
#define GSWIP_MDIO_PHY_FCONTX_MASK 0 x0180
#define GSWIP_MDIO_PHY_FCONTX_AUTO 0 x0000
#define GSWIP_MDIO_PHY_FCONTX_EN 0 x0100
#define GSWIP_MDIO_PHY_FCONTX_DIS 0 x0180
#define GSWIP_MDIO_PHY_FCONRX_MASK 0 x0060
#define GSWIP_MDIO_PHY_FCONRX_AUTO 0 x0000
#define GSWIP_MDIO_PHY_FCONRX_EN 0 x0020
#define GSWIP_MDIO_PHY_FCONRX_DIS 0 x0060
#define GSWIP_MDIO_PHY_ADDR_MASK 0 x001f
#define GSWIP_MDIO_PHY_MASK (GSWIP_MDIO_PHY_ADDR_MASK | \
GSWIP_MDIO_PHY_FCONRX_MASK | \
GSWIP_MDIO_PHY_FCONTX_MASK | \
GSWIP_MDIO_PHY_LINK_MASK | \
GSWIP_MDIO_PHY_SPEED_MASK | \
GSWIP_MDIO_PHY_FDUP_MASK)
/* GSWIP MII Registers */
#define GSWIP_MII_CFGp(p) (0 x2 * (p))
#define GSWIP_MII_CFG_RESET BIT(15 )
#define GSWIP_MII_CFG_EN BIT(14 )
#define GSWIP_MII_CFG_ISOLATE BIT(13 )
#define GSWIP_MII_CFG_LDCLKDIS BIT(12 )
#define GSWIP_MII_CFG_RGMII_IBS BIT(8 )
#define GSWIP_MII_CFG_RMII_CLK BIT(7 )
#define GSWIP_MII_CFG_MODE_MIIP 0 x0
#define GSWIP_MII_CFG_MODE_MIIM 0 x1
#define GSWIP_MII_CFG_MODE_RMIIP 0 x2
#define GSWIP_MII_CFG_MODE_RMIIM 0 x3
#define GSWIP_MII_CFG_MODE_RGMII 0 x4
#define GSWIP_MII_CFG_MODE_GMII 0 x9
#define GSWIP_MII_CFG_MODE_MASK 0 xf
#define GSWIP_MII_CFG_RATE_M2P5 0 x00
#define GSWIP_MII_CFG_RATE_M25 0 x10
#define GSWIP_MII_CFG_RATE_M125 0 x20
#define GSWIP_MII_CFG_RATE_M50 0 x30
#define GSWIP_MII_CFG_RATE_AUTO 0 x40
#define GSWIP_MII_CFG_RATE_MASK 0 x70
#define GSWIP_MII_PCDU0 0 x01
#define GSWIP_MII_PCDU1 0 x03
#define GSWIP_MII_PCDU5 0 x05
#define GSWIP_MII_PCDU_TXDLY_MASK GENMASK(2 , 0 )
#define GSWIP_MII_PCDU_RXDLY_MASK GENMASK(9 , 7 )
/* GSWIP Core Registers */
#define GSWIP_SWRES 0 x000
#define GSWIP_SWRES_R1 BIT(1 ) /* GSWIP Software reset */
#define GSWIP_SWRES_R0 BIT(0 ) /* GSWIP Hardware reset */
#define GSWIP_VERSION 0 x013
#define GSWIP_VERSION_REV_SHIFT 0
#define GSWIP_VERSION_REV_MASK GENMASK(7 , 0 )
#define GSWIP_VERSION_MOD_SHIFT 8
#define GSWIP_VERSION_MOD_MASK GENMASK(15 , 8 )
#define GSWIP_VERSION_2_0 0 x100
#define GSWIP_VERSION_2_1 0 x021
#define GSWIP_VERSION_2_2 0 x122
#define GSWIP_VERSION_2_2_ETC 0 x022
#define GSWIP_BM_RAM_VAL(x) (0 x043 - (x))
#define GSWIP_BM_RAM_ADDR 0 x044
#define GSWIP_BM_RAM_CTRL 0 x045
#define GSWIP_BM_RAM_CTRL_BAS BIT(15 )
#define GSWIP_BM_RAM_CTRL_OPMOD BIT(5 )
#define GSWIP_BM_RAM_CTRL_ADDR_MASK GENMASK(4 , 0 )
#define GSWIP_BM_QUEUE_GCTRL 0 x04A
#define GSWIP_BM_QUEUE_GCTRL_GL_MOD BIT(10 )
/* buffer management Port Configuration Register */
#define GSWIP_BM_PCFGp(p) (0 x080 + ((p) * 2 ))
#define GSWIP_BM_PCFG_CNTEN BIT(0 ) /* RMON Counter Enable */
#define GSWIP_BM_PCFG_IGCNT BIT(1 ) /* Ingres Special Tag RMON count */
/* buffer management Port Control Register */
#define GSWIP_BM_RMON_CTRLp(p) (0 x81 + ((p) * 2 ))
#define GSWIP_BM_CTRL_RMON_RAM1_RES BIT(0 ) /* Software Reset for RMON RAM 1 */
#define GSWIP_BM_CTRL_RMON_RAM2_RES BIT(1 ) /* Software Reset for RMON RAM 2 */
/* PCE */
#define GSWIP_PCE_TBL_KEY(x) (0 x447 - (x))
#define GSWIP_PCE_TBL_MASK 0 x448
#define GSWIP_PCE_TBL_VAL(x) (0 x44D - (x))
#define GSWIP_PCE_TBL_ADDR 0 x44E
#define GSWIP_PCE_TBL_CTRL 0 x44F
#define GSWIP_PCE_TBL_CTRL_BAS BIT(15 )
#define GSWIP_PCE_TBL_CTRL_TYPE BIT(13 )
#define GSWIP_PCE_TBL_CTRL_VLD BIT(12 )
#define GSWIP_PCE_TBL_CTRL_KEYFORM BIT(11 )
#define GSWIP_PCE_TBL_CTRL_GMAP_MASK GENMASK(10 , 7 )
#define GSWIP_PCE_TBL_CTRL_OPMOD_MASK GENMASK(6 , 5 )
#define GSWIP_PCE_TBL_CTRL_OPMOD_ADRD 0 x00
#define GSWIP_PCE_TBL_CTRL_OPMOD_ADWR 0 x20
#define GSWIP_PCE_TBL_CTRL_OPMOD_KSRD 0 x40
#define GSWIP_PCE_TBL_CTRL_OPMOD_KSWR 0 x60
#define GSWIP_PCE_TBL_CTRL_ADDR_MASK GENMASK(4 , 0 )
#define GSWIP_PCE_PMAP1 0 x453 /* Monitoring port map */
#define GSWIP_PCE_PMAP2 0 x454 /* Default Multicast port map */
#define GSWIP_PCE_PMAP3 0 x455 /* Default Unknown Unicast port map */
#define GSWIP_PCE_GCTRL_0 0 x456
#define GSWIP_PCE_GCTRL_0_MTFL BIT(0 ) /* MAC Table Flushing */
#define GSWIP_PCE_GCTRL_0_MC_VALID BIT(3 )
#define GSWIP_PCE_GCTRL_0_VLAN BIT(14 ) /* VLAN aware Switching */
#define GSWIP_PCE_GCTRL_1 0 x457
#define GSWIP_PCE_GCTRL_1_MAC_GLOCK BIT(2 ) /* MAC Address table lock */
#define GSWIP_PCE_GCTRL_1_MAC_GLOCK_MOD BIT(3 ) /* Mac address table lock forwarding mode */
#define GSWIP_PCE_PCTRL_0p(p) (0 x480 + ((p) * 0 xA))
#define GSWIP_PCE_PCTRL_0_TVM BIT(5 ) /* Transparent VLAN mode */
#define GSWIP_PCE_PCTRL_0_VREP BIT(6 ) /* VLAN Replace Mode */
#define GSWIP_PCE_PCTRL_0_INGRESS BIT(11 ) /* Accept special tag in ingress */
#define GSWIP_PCE_PCTRL_0_PSTATE_LISTEN 0 x0
#define GSWIP_PCE_PCTRL_0_PSTATE_RX 0 x1
#define GSWIP_PCE_PCTRL_0_PSTATE_TX 0 x2
#define GSWIP_PCE_PCTRL_0_PSTATE_LEARNING 0 x3
#define GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING 0 x7
#define GSWIP_PCE_PCTRL_0_PSTATE_MASK GENMASK(2 , 0 )
#define GSWIP_PCE_VCTRL(p) (0 x485 + ((p) * 0 xA))
#define GSWIP_PCE_VCTRL_UVR BIT(0 ) /* Unknown VLAN Rule */
#define GSWIP_PCE_VCTRL_VIMR BIT(3 ) /* VLAN Ingress Member violation rule */
#define GSWIP_PCE_VCTRL_VEMR BIT(4 ) /* VLAN Egress Member violation rule */
#define GSWIP_PCE_VCTRL_VSR BIT(5 ) /* VLAN Security */
#define GSWIP_PCE_VCTRL_VID0 BIT(6 ) /* Priority Tagged Rule */
#define GSWIP_PCE_DEFPVID(p) (0 x486 + ((p) * 0 xA))
#define GSWIP_MAC_FLEN 0 x8C5
#define GSWIP_MAC_CTRL_0p(p) (0 x903 + ((p) * 0 xC))
#define GSWIP_MAC_CTRL_0_PADEN BIT(8 )
#define GSWIP_MAC_CTRL_0_FCS_EN BIT(7 )
#define GSWIP_MAC_CTRL_0_FCON_MASK 0 x0070
#define GSWIP_MAC_CTRL_0_FCON_AUTO 0 x0000
#define GSWIP_MAC_CTRL_0_FCON_RX 0 x0010
#define GSWIP_MAC_CTRL_0_FCON_TX 0 x0020
#define GSWIP_MAC_CTRL_0_FCON_RXTX 0 x0030
#define GSWIP_MAC_CTRL_0_FCON_NONE 0 x0040
#define GSWIP_MAC_CTRL_0_FDUP_MASK 0 x000C
#define GSWIP_MAC_CTRL_0_FDUP_AUTO 0 x0000
#define GSWIP_MAC_CTRL_0_FDUP_EN 0 x0004
#define GSWIP_MAC_CTRL_0_FDUP_DIS 0 x000C
#define GSWIP_MAC_CTRL_0_GMII_MASK 0 x0003
#define GSWIP_MAC_CTRL_0_GMII_AUTO 0 x0000
#define GSWIP_MAC_CTRL_0_GMII_MII 0 x0001
#define GSWIP_MAC_CTRL_0_GMII_RGMII 0 x0002
#define GSWIP_MAC_CTRL_2p(p) (0 x905 + ((p) * 0 xC))
#define GSWIP_MAC_CTRL_2_LCHKL BIT(2 ) /* Frame Length Check Long Enable */
#define GSWIP_MAC_CTRL_2_MLEN BIT(3 ) /* Maximum Untagged Frame Lnegth */
/* Ethernet Switch Fetch DMA Port Control Register */
#define GSWIP_FDMA_PCTRLp(p) (0 xA80 + ((p) * 0 x6))
#define GSWIP_FDMA_PCTRL_EN BIT(0 ) /* FDMA Port Enable */
#define GSWIP_FDMA_PCTRL_STEN BIT(1 ) /* Special Tag Insertion Enable */
#define GSWIP_FDMA_PCTRL_VLANMOD_MASK GENMASK(4 , 3 ) /* VLAN Modification Control */
#define GSWIP_FDMA_PCTRL_VLANMOD_SHIFT 3 /* VLAN Modification Control */
#define GSWIP_FDMA_PCTRL_VLANMOD_DIS (0 x0 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT)
#define GSWIP_FDMA_PCTRL_VLANMOD_PRIO (0 x1 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT)
#define GSWIP_FDMA_PCTRL_VLANMOD_ID (0 x2 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT)
#define GSWIP_FDMA_PCTRL_VLANMOD_BOTH (0 x3 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT)
/* Ethernet Switch Store DMA Port Control Register */
#define GSWIP_SDMA_PCTRLp(p) (0 xBC0 + ((p) * 0 x6))
#define GSWIP_SDMA_PCTRL_EN BIT(0 ) /* SDMA Port Enable */
#define GSWIP_SDMA_PCTRL_FCEN BIT(1 ) /* Flow Control Enable */
#define GSWIP_SDMA_PCTRL_PAUFWD BIT(3 ) /* Pause Frame Forwarding */
#define GSWIP_TABLE_ACTIVE_VLAN 0 x01
#define GSWIP_TABLE_VLAN_MAPPING 0 x02
#define GSWIP_TABLE_MAC_BRIDGE 0 x0b
#define GSWIP_TABLE_MAC_BRIDGE_KEY3_FID GENMASK(5 , 0 ) /* Filtering identifier */
#define GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT GENMASK(7 , 4 ) /* Port on learned entries */
#define GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC BIT(0 ) /* Static, non-aging entry */
#define XRX200_GPHY_FW_ALIGN (16 * 1024 )
/* Maximum packet size supported by the switch. In theory this should be 10240,
* but long packets currently cause lock - ups with an MTU of over 2526 . Medium
* packets are sometimes dropped ( e . g . TCP over 2477 , UDP over 2516 - 2519 , ICMP
* over 2526 ) , hence an MTU value of 2400 seems safe . This issue only affects
* packet reception . This is probably caused by the PPA engine , which is on the
* RX part of the device . Packet transmission works properly up to 10240 .
*/
#define GSWIP_MAX_PACKET_LENGTH 2400
struct gswip_hw_info {
int max_ports;
int cpu_port;
const struct dsa_switch_ops *ops;
};
struct xway_gphy_match_data {
char *fe_firmware_name;
char *ge_firmware_name;
};
struct gswip_gphy_fw {
struct clk *clk_gate;
struct reset_control *reset;
u32 fw_addr_offset;
char *fw_name;
};
struct gswip_vlan {
struct net_device *bridge;
u16 vid;
u8 fid;
};
struct gswip_priv {
__iomem void *gswip;
__iomem void *mdio;
__iomem void *mii;
const struct gswip_hw_info *hw_info;
const struct xway_gphy_match_data *gphy_fw_name_cfg;
struct dsa_switch *ds;
struct device *dev;
struct regmap *rcu_regmap;
struct gswip_vlan vlans[64 ];
int num_gphy_fw;
struct gswip_gphy_fw *gphy_fw;
u32 port_vlan_filter;
struct mutex pce_table_lock;
};
struct gswip_pce_table_entry {
u16 index; // PCE_TBL_ADDR.ADDR = pData->table_index
u16 table; // PCE_TBL_CTRL.ADDR = pData->table
u16 key[8 ];
u16 val[5 ];
u16 mask;
u8 gmap;
bool type;
bool valid;
bool key_mode;
};
struct gswip_rmon_cnt_desc {
unsigned int size;
unsigned int offset;
const char *name;
};
#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name}
static const struct gswip_rmon_cnt_desc gswip_rmon_cnt[] = {
/** Receive Packet Count (only packets that are accepted and not discarded). */
MIB_DESC(1 , 0 x1F, "RxGoodPkts" ),
MIB_DESC(1 , 0 x23, "RxUnicastPkts" ),
MIB_DESC(1 , 0 x22, "RxMulticastPkts" ),
MIB_DESC(1 , 0 x21, "RxFCSErrorPkts" ),
MIB_DESC(1 , 0 x1D, "RxUnderSizeGoodPkts" ),
MIB_DESC(1 , 0 x1E, "RxUnderSizeErrorPkts" ),
MIB_DESC(1 , 0 x1B, "RxOversizeGoodPkts" ),
MIB_DESC(1 , 0 x1C, "RxOversizeErrorPkts" ),
MIB_DESC(1 , 0 x20, "RxGoodPausePkts" ),
MIB_DESC(1 , 0 x1A, "RxAlignErrorPkts" ),
MIB_DESC(1 , 0 x12, "Rx64BytePkts" ),
MIB_DESC(1 , 0 x13, "Rx127BytePkts" ),
MIB_DESC(1 , 0 x14, "Rx255BytePkts" ),
MIB_DESC(1 , 0 x15, "Rx511BytePkts" ),
MIB_DESC(1 , 0 x16, "Rx1023BytePkts" ),
/** Receive Size 1024-1522 (or more, if configured) Packet Count. */
MIB_DESC(1 , 0 x17, "RxMaxBytePkts" ),
MIB_DESC(1 , 0 x18, "RxDroppedPkts" ),
MIB_DESC(1 , 0 x19, "RxFilteredPkts" ),
MIB_DESC(2 , 0 x24, "RxGoodBytes" ),
MIB_DESC(2 , 0 x26, "RxBadBytes" ),
MIB_DESC(1 , 0 x11, "TxAcmDroppedPkts" ),
MIB_DESC(1 , 0 x0C, "TxGoodPkts" ),
MIB_DESC(1 , 0 x06, "TxUnicastPkts" ),
MIB_DESC(1 , 0 x07, "TxMulticastPkts" ),
MIB_DESC(1 , 0 x00, "Tx64BytePkts" ),
MIB_DESC(1 , 0 x01, "Tx127BytePkts" ),
MIB_DESC(1 , 0 x02, "Tx255BytePkts" ),
MIB_DESC(1 , 0 x03, "Tx511BytePkts" ),
MIB_DESC(1 , 0 x04, "Tx1023BytePkts" ),
/** Transmit Size 1024-1522 (or more, if configured) Packet Count. */
MIB_DESC(1 , 0 x05, "TxMaxBytePkts" ),
MIB_DESC(1 , 0 x08, "TxSingleCollCount" ),
MIB_DESC(1 , 0 x09, "TxMultCollCount" ),
MIB_DESC(1 , 0 x0A, "TxLateCollCount" ),
MIB_DESC(1 , 0 x0B, "TxExcessCollCount" ),
MIB_DESC(1 , 0 x0D, "TxPauseCount" ),
MIB_DESC(1 , 0 x10, "TxDroppedPkts" ),
MIB_DESC(2 , 0 x0E, "TxGoodBytes" ),
};
static u32 gswip_switch_r(struct gswip_priv *priv, u32 offset)
{
return __raw_readl(priv->gswip + (offset * 4 ));
}
static void gswip_switch_w(struct gswip_priv *priv, u32 val, u32 offset)
{
__raw_writel(val, priv->gswip + (offset * 4 ));
}
static void gswip_switch_mask(struct gswip_priv *priv, u32 clear, u32 set,
u32 offset)
{
u32 val = gswip_switch_r(priv, offset);
val &= ~(clear);
val |= set;
gswip_switch_w(priv, val, offset);
}
static u32 gswip_switch_r_timeout(struct gswip_priv *priv, u32 offset,
u32 cleared)
{
u32 val;
return readx_poll_timeout(__raw_readl, priv->gswip + (offset * 4 ), val,
(val & cleared) == 0 , 20 , 50000 );
}
static u32 gswip_mdio_r(struct gswip_priv *priv, u32 offset)
{
return __raw_readl(priv->mdio + (offset * 4 ));
}
static void gswip_mdio_w(struct gswip_priv *priv, u32 val, u32 offset)
{
__raw_writel(val, priv->mdio + (offset * 4 ));
}
static void gswip_mdio_mask(struct gswip_priv *priv, u32 clear, u32 set,
u32 offset)
{
u32 val = gswip_mdio_r(priv, offset);
val &= ~(clear);
val |= set;
gswip_mdio_w(priv, val, offset);
}
static u32 gswip_mii_r(struct gswip_priv *priv, u32 offset)
{
return __raw_readl(priv->mii + (offset * 4 ));
}
static void gswip_mii_w(struct gswip_priv *priv, u32 val, u32 offset)
{
__raw_writel(val, priv->mii + (offset * 4 ));
}
static void gswip_mii_mask(struct gswip_priv *priv, u32 clear, u32 set,
u32 offset)
{
u32 val = gswip_mii_r(priv, offset);
val &= ~(clear);
val |= set;
gswip_mii_w(priv, val, offset);
}
static void gswip_mii_mask_cfg(struct gswip_priv *priv, u32 clear, u32 set,
int port)
{
/* There's no MII_CFG register for the CPU port */
if (!dsa_is_cpu_port(priv->ds, port))
gswip_mii_mask(priv, clear, set, GSWIP_MII_CFGp(port));
}
static void gswip_mii_mask_pcdu(struct gswip_priv *priv, u32 clear, u32 set,
int port)
{
switch (port) {
case 0 :
gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU0);
break ;
case 1 :
gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU1);
break ;
case 5 :
gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU5);
break ;
}
}
static int gswip_mdio_poll(struct gswip_priv *priv)
{
int cnt = 100 ;
while (likely(cnt--)) {
u32 ctrl = gswip_mdio_r(priv, GSWIP_MDIO_CTRL);
if ((ctrl & GSWIP_MDIO_CTRL_BUSY) == 0 )
return 0 ;
usleep_range(20 , 40 );
}
return -ETIMEDOUT;
}
static int gswip_mdio_wr(struct mii_bus *bus, int addr, int reg, u16 val)
{
struct gswip_priv *priv = bus->priv;
int err;
err = gswip_mdio_poll(priv);
if (err) {
dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n" );
return err;
}
gswip_mdio_w(priv, val, GSWIP_MDIO_WRITE);
gswip_mdio_w(priv, GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_WR |
((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
(reg & GSWIP_MDIO_CTRL_REGAD_MASK),
GSWIP_MDIO_CTRL);
return 0 ;
}
static int gswip_mdio_rd(struct mii_bus *bus, int addr, int reg)
{
struct gswip_priv *priv = bus->priv;
int err;
err = gswip_mdio_poll(priv);
if (err) {
dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n" );
return err;
}
gswip_mdio_w(priv, GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_RD |
((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
(reg & GSWIP_MDIO_CTRL_REGAD_MASK),
GSWIP_MDIO_CTRL);
err = gswip_mdio_poll(priv);
if (err) {
dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n" );
return err;
}
return gswip_mdio_r(priv, GSWIP_MDIO_READ);
}
static int gswip_mdio(struct gswip_priv *priv)
{
struct device_node *mdio_np, *switch_np = priv->dev->of_node;
struct device *dev = priv->dev;
struct mii_bus *bus;
int err = 0 ;
mdio_np = of_get_compatible_child(switch_np, "lantiq,xrx200-mdio" );
if (!of_device_is_available(mdio_np))
goto out_put_node;
bus = devm_mdiobus_alloc(dev);
if (!bus) {
err = -ENOMEM;
goto out_put_node;
}
bus->priv = priv;
bus->read = gswip_mdio_rd;
bus->write = gswip_mdio_wr;
bus->name = "lantiq,xrx200-mdio" ;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii" , dev_name(priv->dev));
bus->parent = priv->dev;
err = devm_of_mdiobus_register(dev, bus, mdio_np);
out_put_node:
of_node_put(mdio_np);
return err;
}
static int gswip_pce_table_entry_read(struct gswip_priv *priv,
struct gswip_pce_table_entry *tbl)
{
int i;
int err;
u16 crtl;
u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSRD :
GSWIP_PCE_TBL_CTRL_OPMOD_ADRD;
mutex_lock(&priv->pce_table_lock);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
GSWIP_PCE_TBL_CTRL_BAS);
if (err) {
mutex_unlock(&priv->pce_table_lock);
return err;
}
gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR);
gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
tbl->table | addr_mode | GSWIP_PCE_TBL_CTRL_BAS,
GSWIP_PCE_TBL_CTRL);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
GSWIP_PCE_TBL_CTRL_BAS);
if (err) {
mutex_unlock(&priv->pce_table_lock);
return err;
}
for (i = 0 ; i < ARRAY_SIZE(tbl->key); i++)
tbl->key[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_KEY(i));
for (i = 0 ; i < ARRAY_SIZE(tbl->val); i++)
tbl->val[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_VAL(i));
tbl->mask = gswip_switch_r(priv, GSWIP_PCE_TBL_MASK);
crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL);
tbl->type = !!(crtl & GSWIP_PCE_TBL_CTRL_TYPE);
tbl->valid = !!(crtl & GSWIP_PCE_TBL_CTRL_VLD);
tbl->gmap = (crtl & GSWIP_PCE_TBL_CTRL_GMAP_MASK) >> 7 ;
mutex_unlock(&priv->pce_table_lock);
return 0 ;
}
static int gswip_pce_table_entry_write(struct gswip_priv *priv,
struct gswip_pce_table_entry *tbl)
{
int i;
int err;
u16 crtl;
u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSWR :
GSWIP_PCE_TBL_CTRL_OPMOD_ADWR;
mutex_lock(&priv->pce_table_lock);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
GSWIP_PCE_TBL_CTRL_BAS);
if (err) {
mutex_unlock(&priv->pce_table_lock);
return err;
}
gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR);
gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
tbl->table | addr_mode,
GSWIP_PCE_TBL_CTRL);
for (i = 0 ; i < ARRAY_SIZE(tbl->key); i++)
gswip_switch_w(priv, tbl->key[i], GSWIP_PCE_TBL_KEY(i));
for (i = 0 ; i < ARRAY_SIZE(tbl->val); i++)
gswip_switch_w(priv, tbl->val[i], GSWIP_PCE_TBL_VAL(i));
gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
tbl->table | addr_mode,
GSWIP_PCE_TBL_CTRL);
gswip_switch_w(priv, tbl->mask, GSWIP_PCE_TBL_MASK);
crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL);
crtl &= ~(GSWIP_PCE_TBL_CTRL_TYPE | GSWIP_PCE_TBL_CTRL_VLD |
GSWIP_PCE_TBL_CTRL_GMAP_MASK);
if (tbl->type)
crtl |= GSWIP_PCE_TBL_CTRL_TYPE;
if (tbl->valid)
crtl |= GSWIP_PCE_TBL_CTRL_VLD;
crtl |= (tbl->gmap << 7 ) & GSWIP_PCE_TBL_CTRL_GMAP_MASK;
crtl |= GSWIP_PCE_TBL_CTRL_BAS;
gswip_switch_w(priv, crtl, GSWIP_PCE_TBL_CTRL);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
GSWIP_PCE_TBL_CTRL_BAS);
mutex_unlock(&priv->pce_table_lock);
return err;
}
/* Add the LAN port into a bridge with the CPU port by
* default . This prevents automatic forwarding of
* packages between the LAN ports when no explicit
* bridge is configured .
*/
static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)
{
struct gswip_pce_table_entry vlan_active = {0 ,};
struct gswip_pce_table_entry vlan_mapping = {0 ,};
unsigned int cpu_port = priv->hw_info->cpu_port;
int err;
vlan_active.index = port + 1 ;
vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
vlan_active.key[0 ] = 0 ; /* vid */
vlan_active.val[0 ] = port + 1 /* fid */;
vlan_active.valid = add;
err = gswip_pce_table_entry_write(priv, &vlan_active);
if (err) {
dev_err(priv->dev, "failed to write active VLAN: %d\n" , err);
return err;
}
if (!add)
return 0 ;
vlan_mapping.index = port + 1 ;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
vlan_mapping.val[0 ] = 0 /* vid */;
vlan_mapping.val[1 ] = BIT(port) | BIT(cpu_port);
vlan_mapping.val[2 ] = 0 ;
err = gswip_pce_table_entry_write(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to write VLAN mapping: %d\n" , err);
return err;
}
return 0 ;
}
static int gswip_port_setup(struct dsa_switch *ds, int port)
{
struct gswip_priv *priv = ds->priv;
int err;
if (!dsa_is_cpu_port(ds, port)) {
err = gswip_add_single_port_br(priv, port, true );
if (err)
return err;
}
return 0 ;
}
static int gswip_port_enable(struct dsa_switch *ds, int port,
struct phy_device *phydev)
{
struct gswip_priv *priv = ds->priv;
if (!dsa_is_cpu_port(ds, port)) {
u32 mdio_phy = 0 ;
if (phydev)
mdio_phy = phydev->mdio.addr & GSWIP_MDIO_PHY_ADDR_MASK;
gswip_mdio_mask(priv, GSWIP_MDIO_PHY_ADDR_MASK, mdio_phy,
GSWIP_MDIO_PHYp(port));
}
/* RMON Counter Enable for port */
gswip_switch_w(priv, GSWIP_BM_PCFG_CNTEN, GSWIP_BM_PCFGp(port));
/* enable port fetch/store dma & VLAN Modification */
gswip_switch_mask(priv, 0 , GSWIP_FDMA_PCTRL_EN |
GSWIP_FDMA_PCTRL_VLANMOD_BOTH,
GSWIP_FDMA_PCTRLp(port));
gswip_switch_mask(priv, 0 , GSWIP_SDMA_PCTRL_EN,
GSWIP_SDMA_PCTRLp(port));
return 0 ;
}
static void gswip_port_disable(struct dsa_switch *ds, int port)
{
struct gswip_priv *priv = ds->priv;
gswip_switch_mask(priv, GSWIP_FDMA_PCTRL_EN, 0 ,
GSWIP_FDMA_PCTRLp(port));
gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0 ,
GSWIP_SDMA_PCTRLp(port));
}
static int gswip_pce_load_microcode(struct gswip_priv *priv)
{
int i;
int err;
gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
GSWIP_PCE_TBL_CTRL_OPMOD_ADWR, GSWIP_PCE_TBL_CTRL);
gswip_switch_w(priv, 0 , GSWIP_PCE_TBL_MASK);
for (i = 0 ; i < ARRAY_SIZE(gswip_pce_microcode); i++) {
gswip_switch_w(priv, i, GSWIP_PCE_TBL_ADDR);
gswip_switch_w(priv, gswip_pce_microcode[i].val_0,
GSWIP_PCE_TBL_VAL(0 ));
gswip_switch_w(priv, gswip_pce_microcode[i].val_1,
GSWIP_PCE_TBL_VAL(1 ));
gswip_switch_w(priv, gswip_pce_microcode[i].val_2,
GSWIP_PCE_TBL_VAL(2 ));
gswip_switch_w(priv, gswip_pce_microcode[i].val_3,
GSWIP_PCE_TBL_VAL(3 ));
/* start the table access: */
gswip_switch_mask(priv, 0 , GSWIP_PCE_TBL_CTRL_BAS,
GSWIP_PCE_TBL_CTRL);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
GSWIP_PCE_TBL_CTRL_BAS);
if (err)
return err;
}
/* tell the switch that the microcode is loaded */
gswip_switch_mask(priv, 0 , GSWIP_PCE_GCTRL_0_MC_VALID,
GSWIP_PCE_GCTRL_0);
return 0 ;
}
static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering,
struct netlink_ext_ack *extack)
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
/* Do not allow changing the VLAN filtering options while in bridge */
if (bridge && !!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) {
NL_SET_ERR_MSG_MOD(extack,
"Dynamic toggling of vlan_filtering not supported" );
return -EIO;
}
if (vlan_filtering) {
/* Use tag based VLAN */
gswip_switch_mask(priv,
GSWIP_PCE_VCTRL_VSR,
GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
GSWIP_PCE_VCTRL_VEMR,
GSWIP_PCE_VCTRL(port));
gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_TVM, 0 ,
GSWIP_PCE_PCTRL_0p(port));
} else {
/* Use port based VLAN */
gswip_switch_mask(priv,
GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
GSWIP_PCE_VCTRL_VEMR,
GSWIP_PCE_VCTRL_VSR,
GSWIP_PCE_VCTRL(port));
gswip_switch_mask(priv, 0 , GSWIP_PCE_PCTRL_0_TVM,
GSWIP_PCE_PCTRL_0p(port));
}
return 0 ;
}
static int gswip_setup(struct dsa_switch *ds)
{
struct gswip_priv *priv = ds->priv;
unsigned int cpu_port = priv->hw_info->cpu_port;
int i;
int err;
gswip_switch_w(priv, GSWIP_SWRES_R0, GSWIP_SWRES);
usleep_range(5000 , 10000 );
gswip_switch_w(priv, 0 , GSWIP_SWRES);
/* disable port fetch/store dma on all ports */
for (i = 0 ; i < priv->hw_info->max_ports; i++) {
gswip_port_disable(ds, i);
gswip_port_vlan_filtering(ds, i, false , NULL);
}
/* enable Switch */
gswip_mdio_mask(priv, 0 , GSWIP_MDIO_GLOB_ENABLE, GSWIP_MDIO_GLOB);
err = gswip_pce_load_microcode(priv);
if (err) {
dev_err(priv->dev, "writing PCE microcode failed, %i\n" , err);
return err;
}
/* Default unknown Broadcast/Multicast/Unicast port maps */
gswip_switch_w(priv, BIT(cpu_port), GSWIP_PCE_PMAP1);
gswip_switch_w(priv, BIT(cpu_port), GSWIP_PCE_PMAP2);
gswip_switch_w(priv, BIT(cpu_port), GSWIP_PCE_PMAP3);
/* Deactivate MDIO PHY auto polling. Some PHYs as the AR8030 have an
* interoperability problem with this auto polling mechanism because
* their status registers think that the link is in a different state
* than it actually is . For the AR8030 it has the BMSR_ESTATEN bit set
* as well as ESTATUS_1000_TFULL and ESTATUS_1000_XFULL . This makes the
* auto polling state machine consider the link being negotiated with
* 1 Gbit / s . Since the PHY itself is a Fast Ethernet RMII PHY this leads
* to the switch port being completely dead ( RX and TX are both not
* working ) .
* Also with various other PHY / port combinations ( PHY11G GPHY , PHY22F
* GPHY , external RGMII PEF7071 / 7072 ) any traffic would stop . Sometimes
* it would work fine for a few minutes to hours and then stop , on
* other device it would no traffic could be sent or received at all .
* Testing shows that when PHY auto polling is disabled these problems
* go away .
*/
gswip_mdio_w(priv, 0 x0, GSWIP_MDIO_MDC_CFG0);
/* Configure the MDIO Clock 2.5 MHz */
gswip_mdio_mask(priv, 0 xff, 0 x09, GSWIP_MDIO_MDC_CFG1);
/* Disable the xMII interface and clear it's isolation bit */
for (i = 0 ; i < priv->hw_info->max_ports; i++)
gswip_mii_mask_cfg(priv,
GSWIP_MII_CFG_EN | GSWIP_MII_CFG_ISOLATE,
0 , i);
/* enable special tag insertion on cpu port */
gswip_switch_mask(priv, 0 , GSWIP_FDMA_PCTRL_STEN,
GSWIP_FDMA_PCTRLp(cpu_port));
/* accept special tag in ingress direction */
gswip_switch_mask(priv, 0 , GSWIP_PCE_PCTRL_0_INGRESS,
GSWIP_PCE_PCTRL_0p(cpu_port));
gswip_switch_mask(priv, 0 , GSWIP_BM_QUEUE_GCTRL_GL_MOD,
GSWIP_BM_QUEUE_GCTRL);
/* VLAN aware Switching */
gswip_switch_mask(priv, 0 , GSWIP_PCE_GCTRL_0_VLAN, GSWIP_PCE_GCTRL_0);
/* Flush MAC Table */
gswip_switch_mask(priv, 0 , GSWIP_PCE_GCTRL_0_MTFL, GSWIP_PCE_GCTRL_0);
err = gswip_switch_r_timeout(priv, GSWIP_PCE_GCTRL_0,
GSWIP_PCE_GCTRL_0_MTFL);
if (err) {
dev_err(priv->dev, "MAC flushing didn't finish\n" );
return err;
}
ds->mtu_enforcement_ingress = true ;
ds->configure_vlan_while_not_filtering = false ;
return 0 ;
}
static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
return DSA_TAG_PROTO_GSWIP;
}
static int gswip_vlan_active_create(struct gswip_priv *priv,
struct net_device *bridge,
int fid, u16 vid)
{
struct gswip_pce_table_entry vlan_active = {0 ,};
unsigned int max_ports = priv->hw_info->max_ports;
int idx = -1 ;
int err;
int i;
/* Look for a free slot */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (!priv->vlans[i].bridge) {
idx = i;
break ;
}
}
if (idx == -1 )
return -ENOSPC;
if (fid == -1 )
fid = idx;
vlan_active.index = idx;
vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
vlan_active.key[0 ] = vid;
vlan_active.val[0 ] = fid;
vlan_active.valid = true ;
err = gswip_pce_table_entry_write(priv, &vlan_active);
if (err) {
dev_err(priv->dev, "failed to write active VLAN: %d\n" , err);
return err;
}
priv->vlans[idx].bridge = bridge;
priv->vlans[idx].vid = vid;
priv->vlans[idx].fid = fid;
return idx;
}
static int gswip_vlan_active_remove(struct gswip_priv *priv, int idx)
{
struct gswip_pce_table_entry vlan_active = {0 ,};
int err;
vlan_active.index = idx;
vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
vlan_active.valid = false ;
err = gswip_pce_table_entry_write(priv, &vlan_active);
if (err)
dev_err(priv->dev, "failed to delete active VLAN: %d\n" , err);
priv->vlans[idx].bridge = NULL;
return err;
}
static int gswip_vlan_add_unaware(struct gswip_priv *priv,
struct net_device *bridge, int port)
{
struct gswip_pce_table_entry vlan_mapping = {0 ,};
unsigned int max_ports = priv->hw_info->max_ports;
unsigned int cpu_port = priv->hw_info->cpu_port;
bool active_vlan_created = false ;
int idx = -1 ;
int i;
int err;
/* Check if there is already a page for this bridge */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge) {
idx = i;
break ;
}
}
/* If this bridge is not programmed yet, add a Active VLAN table
* entry in a free slot and prepare the VLAN mapping table entry .
*/
if (idx == -1 ) {
idx = gswip_vlan_active_create(priv, bridge, -1 , 0 );
if (idx < 0 )
return idx;
active_vlan_created = true ;
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
/* VLAN ID byte, maps to the VLAN ID of vlan active table */
vlan_mapping.val[0 ] = 0 ;
} else {
/* Read the existing VLAN mapping entry from the switch */
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
err = gswip_pce_table_entry_read(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to read VLAN mapping: %d\n" ,
err);
return err;
}
}
/* Update the VLAN mapping entry and write it to the switch */
vlan_mapping.val[1 ] |= BIT(cpu_port);
vlan_mapping.val[1 ] |= BIT(port);
err = gswip_pce_table_entry_write(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to write VLAN mapping: %d\n" , err);
/* In case an Active VLAN was creaetd delete it again */
if (active_vlan_created)
gswip_vlan_active_remove(priv, idx);
return err;
}
gswip_switch_w(priv, 0 , GSWIP_PCE_DEFPVID(port));
return 0 ;
}
static int gswip_vlan_add_aware(struct gswip_priv *priv,
struct net_device *bridge, int port,
u16 vid, bool untagged,
bool pvid)
{
struct gswip_pce_table_entry vlan_mapping = {0 ,};
unsigned int max_ports = priv->hw_info->max_ports;
unsigned int cpu_port = priv->hw_info->cpu_port;
bool active_vlan_created = false ;
int idx = -1 ;
int fid = -1 ;
int i;
int err;
/* Check if there is already a page for this bridge */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge) {
if (fid != -1 && fid != priv->vlans[i].fid)
dev_err(priv->dev, "one bridge with multiple flow ids\n" );
fid = priv->vlans[i].fid;
if (priv->vlans[i].vid == vid) {
idx = i;
break ;
}
}
}
/* If this bridge is not programmed yet, add a Active VLAN table
* entry in a free slot and prepare the VLAN mapping table entry .
*/
if (idx == -1 ) {
idx = gswip_vlan_active_create(priv, bridge, fid, vid);
if (idx < 0 )
return idx;
active_vlan_created = true ;
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
/* VLAN ID byte, maps to the VLAN ID of vlan active table */
vlan_mapping.val[0 ] = vid;
} else {
/* Read the existing VLAN mapping entry from the switch */
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
err = gswip_pce_table_entry_read(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to read VLAN mapping: %d\n" ,
err);
return err;
}
}
vlan_mapping.val[0 ] = vid;
/* Update the VLAN mapping entry and write it to the switch */
vlan_mapping.val[1 ] |= BIT(cpu_port);
vlan_mapping.val[2 ] |= BIT(cpu_port);
vlan_mapping.val[1 ] |= BIT(port);
if (untagged)
vlan_mapping.val[2 ] &= ~BIT(port);
else
vlan_mapping.val[2 ] |= BIT(port);
err = gswip_pce_table_entry_write(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to write VLAN mapping: %d\n" , err);
/* In case an Active VLAN was creaetd delete it again */
if (active_vlan_created)
gswip_vlan_active_remove(priv, idx);
return err;
}
if (pvid)
gswip_switch_w(priv, idx, GSWIP_PCE_DEFPVID(port));
return 0 ;
}
static int gswip_vlan_remove(struct gswip_priv *priv,
struct net_device *bridge, int port,
u16 vid, bool pvid, bool vlan_aware)
{
struct gswip_pce_table_entry vlan_mapping = {0 ,};
unsigned int max_ports = priv->hw_info->max_ports;
unsigned int cpu_port = priv->hw_info->cpu_port;
int idx = -1 ;
int i;
int err;
/* Check if there is already a page for this bridge */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge &&
(!vlan_aware || priv->vlans[i].vid == vid)) {
idx = i;
break ;
}
}
if (idx == -1 ) {
dev_err(priv->dev, "bridge to leave does not exists\n" );
return -ENOENT;
}
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
err = gswip_pce_table_entry_read(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to read VLAN mapping: %d\n" , err);
return err;
}
vlan_mapping.val[1 ] &= ~BIT(port);
vlan_mapping.val[2 ] &= ~BIT(port);
err = gswip_pce_table_entry_write(priv, &vlan_mapping);
if (err) {
dev_err(priv->dev, "failed to write VLAN mapping: %d\n" , err);
return err;
}
/* In case all ports are removed from the bridge, remove the VLAN */
if ((vlan_mapping.val[1 ] & ~BIT(cpu_port)) == 0 ) {
err = gswip_vlan_active_remove(priv, idx);
if (err) {
dev_err(priv->dev, "failed to write active VLAN: %d\n" ,
err);
return err;
}
}
/* GSWIP 2.2 (GRX300) and later program here the VID directly. */
if (pvid)
gswip_switch_w(priv, 0 , GSWIP_PCE_DEFPVID(port));
return 0 ;
}
static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
struct dsa_bridge bridge,
bool *tx_fwd_offload,
struct netlink_ext_ack *extack)
{
struct net_device *br = bridge.dev;
struct gswip_priv *priv = ds->priv;
int err;
/* When the bridge uses VLAN filtering we have to configure VLAN
* specific bridges . No bridge is configured here .
*/
if (!br_vlan_enabled(br)) {
err = gswip_vlan_add_unaware(priv, br, port);
if (err)
return err;
priv->port_vlan_filter &= ~BIT(port);
} else {
priv->port_vlan_filter |= BIT(port);
}
return gswip_add_single_port_br(priv, port, false );
}
static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
struct dsa_bridge bridge)
{
struct net_device *br = bridge.dev;
struct gswip_priv *priv = ds->priv;
gswip_add_single_port_br(priv, port, true );
/* When the bridge uses VLAN filtering we have to configure VLAN
* specific bridges . No bridge is configured here .
*/
if (!br_vlan_enabled(br))
gswip_vlan_remove(priv, br, port, 0 , true , false );
}
static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
unsigned int max_ports = priv->hw_info->max_ports;
int pos = max_ports;
int i, idx = -1 ;
/* We only support VLAN filtering on bridges */
if (!dsa_is_cpu_port(ds, port) && !bridge)
return -EOPNOTSUPP;
/* Check if there is already a page for this VLAN */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge &&
priv->vlans[i].vid == vlan->vid) {
idx = i;
break ;
}
}
/* If this VLAN is not programmed yet, we have to reserve
* one entry in the VLAN table . Make sure we start at the
* next position round .
*/
if (idx == -1 ) {
/* Look for a free slot */
for (; pos < ARRAY_SIZE(priv->vlans); pos++) {
if (!priv->vlans[pos].bridge) {
idx = pos;
pos++;
break ;
}
}
if (idx == -1 ) {
NL_SET_ERR_MSG_MOD(extack, "No slot in VLAN table" );
return -ENOSPC;
}
}
return 0 ;
}
static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
int err;
err = gswip_port_vlan_prepare(ds, port, vlan, extack);
if (err)
return err;
/* We have to receive all packets on the CPU port and should not
* do any VLAN filtering here . This is also called with bridge
* NULL and then we do not know for which bridge to configure
* this .
*/
if (dsa_is_cpu_port(ds, port))
return 0 ;
return gswip_vlan_add_aware(priv, bridge, port, vlan->vid,
untagged, pvid);
}
static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
/* We have to receive all packets on the CPU port and should not
* do any VLAN filtering here . This is also called with bridge
* NULL and then we do not know for which bridge to configure
* this .
*/
if (dsa_is_cpu_port(ds, port))
return 0 ;
return gswip_vlan_remove(priv, bridge, port, vlan->vid, pvid, true );
}
static void gswip_port_fast_age(struct dsa_switch *ds, int port)
{
struct gswip_priv *priv = ds->priv;
struct gswip_pce_table_entry mac_bridge = {0 ,};
int i;
int err;
for (i = 0 ; i < 2048 ; i++) {
mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
mac_bridge.index = i;
err = gswip_pce_table_entry_read(priv, &mac_bridge);
if (err) {
dev_err(priv->dev, "failed to read mac bridge: %d\n" ,
err);
return ;
}
if (!mac_bridge.valid)
continue ;
if (mac_bridge.val[1 ] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC)
continue ;
if (port != FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
mac_bridge.val[0 ]))
continue ;
mac_bridge.valid = false ;
err = gswip_pce_table_entry_write(priv, &mac_bridge);
if (err) {
dev_err(priv->dev, "failed to write mac bridge: %d\n" ,
err);
return ;
}
}
}
static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
{
struct gswip_priv *priv = ds->priv;
u32 stp_state;
switch (state) {
case BR_STATE_DISABLED:
gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0 ,
GSWIP_SDMA_PCTRLp(port));
return ;
case BR_STATE_BLOCKING:
case BR_STATE_LISTENING:
stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LISTEN;
break ;
case BR_STATE_LEARNING:
stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LEARNING;
break ;
case BR_STATE_FORWARDING:
stp_state = GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING;
break ;
default :
dev_err(priv->dev, "invalid STP state: %d\n" , state);
return ;
}
gswip_switch_mask(priv, 0 , GSWIP_SDMA_PCTRL_EN,
GSWIP_SDMA_PCTRLp(port));
gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_PSTATE_MASK, stp_state,
GSWIP_PCE_PCTRL_0p(port));
}
static int gswip_port_fdb(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, bool add)
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
struct gswip_pce_table_entry mac_bridge = {0 ,};
unsigned int max_ports = priv->hw_info->max_ports;
int fid = -1 ;
int i;
int err;
/* Operation not supported on the CPU port, don't throw errors */
if (!bridge)
return 0 ;
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge) {
fid = priv->vlans[i].fid;
break ;
}
}
if (fid == -1 ) {
dev_err(priv->dev, "no FID found for bridge %s\n" ,
bridge->name);
return -EINVAL;
}
mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
mac_bridge.key_mode = true ;
mac_bridge.key[0 ] = addr[5 ] | (addr[4 ] << 8 );
mac_bridge.key[1 ] = addr[3 ] | (addr[2 ] << 8 );
mac_bridge.key[2 ] = addr[1 ] | (addr[0 ] << 8 );
mac_bridge.key[3 ] = FIELD_PREP(GSWIP_TABLE_MAC_BRIDGE_KEY3_FID, fid);
mac_bridge.val[0 ] = add ? BIT(port) : 0 ; /* port map */
mac_bridge.val[1 ] = GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC;
mac_bridge.valid = add;
err = gswip_pce_table_entry_write(priv, &mac_bridge);
if (err)
dev_err(priv->dev, "failed to write mac bridge: %d\n" , err);
return err;
}
static int gswip_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
return gswip_port_fdb(ds, port, addr, vid, true );
}
static int gswip_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
return gswip_port_fdb(ds, port, addr, vid, false );
}
static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
struct gswip_priv *priv = ds->priv;
struct gswip_pce_table_entry mac_bridge = {0 ,};
unsigned char addr[ETH_ALEN];
int i;
int err;
for (i = 0 ; i < 2048 ; i++) {
mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
mac_bridge.index = i;
err = gswip_pce_table_entry_read(priv, &mac_bridge);
if (err) {
dev_err(priv->dev,
"failed to read mac bridge entry %d: %d\n" ,
i, err);
return err;
}
if (!mac_bridge.valid)
continue ;
addr[5 ] = mac_bridge.key[0 ] & 0 xff;
addr[4 ] = (mac_bridge.key[0 ] >> 8 ) & 0 xff;
addr[3 ] = mac_bridge.key[1 ] & 0 xff;
addr[2 ] = (mac_bridge.key[1 ] >> 8 ) & 0 xff;
addr[1 ] = mac_bridge.key[2 ] & 0 xff;
addr[0 ] = (mac_bridge.key[2 ] >> 8 ) & 0 xff;
if (mac_bridge.val[1 ] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC) {
if (mac_bridge.val[0 ] & BIT(port)) {
err = cb(addr, 0 , true , data);
if (err)
return err;
}
} else {
if (port == FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
mac_bridge.val[0 ])) {
err = cb(addr, 0 , false , data);
if (err)
return err;
}
}
}
return 0 ;
}
static int gswip_port_max_mtu(struct dsa_switch *ds, int port)
{
/* Includes 8 bytes for special header. */
return GSWIP_MAX_PACKET_LENGTH - VLAN_ETH_HLEN - ETH_FCS_LEN;
}
static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{
struct gswip_priv *priv = ds->priv;
/* CPU port always has maximum mtu of user ports, so use it to set
* switch frame size , including 8 byte special header .
*/
if (dsa_is_cpu_port(ds, port)) {
new_mtu += 8 ;
gswip_switch_w(priv, VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN,
GSWIP_MAC_FLEN);
}
/* Enable MLEN for ports with non-standard MTUs, including the special
* header on the CPU port added above .
*/
if (new_mtu != ETH_DATA_LEN)
gswip_switch_mask(priv, 0 , GSWIP_MAC_CTRL_2_MLEN,
GSWIP_MAC_CTRL_2p(port));
else
gswip_switch_mask(priv, GSWIP_MAC_CTRL_2_MLEN, 0 ,
GSWIP_MAC_CTRL_2p(port));
return 0 ;
}
static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
switch (port) {
case 0 :
case 1 :
phy_interface_set_rgmii(config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_MII,
config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_REVMII,
config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_RMII,
config->supported_interfaces);
break ;
case 2 :
case 3 :
case 4 :
case 6 :
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break ;
case 5 :
phy_interface_set_rgmii(config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break ;
}
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000;
}
static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
switch (port) {
case 0 :
phy_interface_set_rgmii(config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_GMII,
config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_RMII,
config->supported_interfaces);
break ;
case 1 :
case 2 :
case 3 :
case 4 :
case 6 :
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
break ;
case 5 :
phy_interface_set_rgmii(config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_RMII,
config->supported_interfaces);
break ;
}
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000;
}
static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link)
{
u32 mdio_phy;
if (link)
mdio_phy = GSWIP_MDIO_PHY_LINK_UP;
else
mdio_phy = GSWIP_MDIO_PHY_LINK_DOWN;
gswip_mdio_mask(priv, GSWIP_MDIO_PHY_LINK_MASK, mdio_phy,
GSWIP_MDIO_PHYp(port));
}
static void gswip_port_set_speed(struct gswip_priv *priv, int port, int speed,
phy_interface_t interface)
{
u32 mdio_phy = 0 , mii_cfg = 0 , mac_ctrl_0 = 0 ;
switch (speed) {
case SPEED_10:
mdio_phy = GSWIP_MDIO_PHY_SPEED_M10;
if (interface == PHY_INTERFACE_MODE_RMII)
mii_cfg = GSWIP_MII_CFG_RATE_M50;
else
mii_cfg = GSWIP_MII_CFG_RATE_M2P5;
mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
break ;
case SPEED_100:
mdio_phy = GSWIP_MDIO_PHY_SPEED_M100;
if (interface == PHY_INTERFACE_MODE_RMII)
mii_cfg = GSWIP_MII_CFG_RATE_M50;
else
mii_cfg = GSWIP_MII_CFG_RATE_M25;
mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
break ;
case SPEED_1000:
mdio_phy = GSWIP_MDIO_PHY_SPEED_G1;
mii_cfg = GSWIP_MII_CFG_RATE_M125;
mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_RGMII;
break ;
}
gswip_mdio_mask(priv, GSWIP_MDIO_PHY_SPEED_MASK, mdio_phy,
GSWIP_MDIO_PHYp(port));
gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_RATE_MASK, mii_cfg, port);
gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_GMII_MASK, mac_ctrl_0,
GSWIP_MAC_CTRL_0p(port));
}
static void gswip_port_set_duplex(struct gswip_priv *priv, int port, int duplex)
{
u32 mac_ctrl_0, mdio_phy;
if (duplex == DUPLEX_FULL) {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_EN;
mdio_phy = GSWIP_MDIO_PHY_FDUP_EN;
} else {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_DIS;
mdio_phy = GSWIP_MDIO_PHY_FDUP_DIS;
}
gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_FDUP_MASK, mac_ctrl_0,
GSWIP_MAC_CTRL_0p(port));
gswip_mdio_mask(priv, GSWIP_MDIO_PHY_FDUP_MASK, mdio_phy,
GSWIP_MDIO_PHYp(port));
}
static void gswip_port_set_pause(struct gswip_priv *priv, int port,
bool tx_pause, bool rx_pause)
{
u32 mac_ctrl_0, mdio_phy;
if (tx_pause && rx_pause) {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RXTX;
mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
GSWIP_MDIO_PHY_FCONRX_EN;
} else if (tx_pause) {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_TX;
mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
GSWIP_MDIO_PHY_FCONRX_DIS;
} else if (rx_pause) {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RX;
mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
GSWIP_MDIO_PHY_FCONRX_EN;
} else {
mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_NONE;
mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
GSWIP_MDIO_PHY_FCONRX_DIS;
}
gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_FCON_MASK,
mac_ctrl_0, GSWIP_MAC_CTRL_0p(port));
gswip_mdio_mask(priv,
GSWIP_MDIO_PHY_FCONTX_MASK |
GSWIP_MDIO_PHY_FCONRX_MASK,
mdio_phy, GSWIP_MDIO_PHYp(port));
}
static void gswip_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct gswip_priv *priv = dp->ds->priv;
int port = dp->index;
u32 miicfg = 0 ;
miicfg |= GSWIP_MII_CFG_LDCLKDIS;
switch (state->interface) {
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_INTERNAL:
miicfg |= GSWIP_MII_CFG_MODE_MIIM;
break ;
case PHY_INTERFACE_MODE_REVMII:
miicfg |= GSWIP_MII_CFG_MODE_MIIP;
break ;
case PHY_INTERFACE_MODE_RMII:
miicfg |= GSWIP_MII_CFG_MODE_RMIIM;
break ;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
miicfg |= GSWIP_MII_CFG_MODE_RGMII;
break ;
case PHY_INTERFACE_MODE_GMII:
miicfg |= GSWIP_MII_CFG_MODE_GMII;
break ;
default :
dev_err(dp->ds->dev,
"Unsupported interface: %d\n" , state->interface);
return ;
}
gswip_mii_mask_cfg(priv,
GSWIP_MII_CFG_MODE_MASK | GSWIP_MII_CFG_RMII_CLK |
GSWIP_MII_CFG_RGMII_IBS | GSWIP_MII_CFG_LDCLKDIS,
miicfg, port);
switch (state->interface) {
case PHY_INTERFACE_MODE_RGMII_ID:
gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_TXDLY_MASK |
GSWIP_MII_PCDU_RXDLY_MASK, 0 , port);
break ;
case PHY_INTERFACE_MODE_RGMII_RXID:
gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_RXDLY_MASK, 0 , port);
break ;
case PHY_INTERFACE_MODE_RGMII_TXID:
gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_TXDLY_MASK, 0 , port);
break ;
default :
break ;
}
}
static void gswip_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct gswip_priv *priv = dp->ds->priv;
int port = dp->index;
gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0 , port);
if (!dsa_port_is_cpu(dp))
gswip_port_set_link(priv, port, false );
}
static void gswip_phylink_mac_link_up(struct phylink_config *config,
struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct dsa_port *dp = dsa_phylink_to_port(config);
struct gswip_priv *priv = dp->ds->priv;
int port = dp->index;
if (!dsa_port_is_cpu(dp)) {
gswip_port_set_link(priv, port, true );
gswip_port_set_speed(priv, port, speed, interface);
gswip_port_set_duplex(priv, port, duplex);
gswip_port_set_pause(priv, port, tx_pause, rx_pause);
}
gswip_mii_mask_cfg(priv, 0 , GSWIP_MII_CFG_EN, port);
}
static void gswip_get_strings(struct dsa_switch *ds, int port, u32 stringset,
uint8_t *data)
{
int i;
if (stringset != ETH_SS_STATS)
return ;
for (i = 0 ; i < ARRAY_SIZE(gswip_rmon_cnt); i++)
ethtool_puts(&data, gswip_rmon_cnt[i].name);
}
static u32 gswip_bcm_ram_entry_read(struct gswip_priv *priv, u32 table,
u32 index)
{
u32 result;
int err;
gswip_switch_w(priv, index, GSWIP_BM_RAM_ADDR);
gswip_switch_mask(priv, GSWIP_BM_RAM_CTRL_ADDR_MASK |
GSWIP_BM_RAM_CTRL_OPMOD,
table | GSWIP_BM_RAM_CTRL_BAS,
GSWIP_BM_RAM_CTRL);
err = gswip_switch_r_timeout(priv, GSWIP_BM_RAM_CTRL,
GSWIP_BM_RAM_CTRL_BAS);
if (err) {
dev_err(priv->dev, "timeout while reading table: %u, index: %u\n" ,
table, index);
return 0 ;
}
result = gswip_switch_r(priv, GSWIP_BM_RAM_VAL(0 ));
result |= gswip_switch_r(priv, GSWIP_BM_RAM_VAL(1 )) << 16 ;
return result;
}
static void gswip_get_ethtool_stats(struct dsa_switch *ds, int port,
uint64_t *data)
{
struct gswip_priv *priv = ds->priv;
const struct gswip_rmon_cnt_desc *rmon_cnt;
int i;
u64 high;
for (i = 0 ; i < ARRAY_SIZE(gswip_rmon_cnt); i++) {
rmon_cnt = &gswip_rmon_cnt[i];
data[i] = gswip_bcm_ram_entry_read(priv, port,
rmon_cnt->offset);
if (rmon_cnt->size == 2 ) {
high = gswip_bcm_ram_entry_read(priv, port,
rmon_cnt->offset + 1 );
data[i] |= high << 32 ;
}
}
}
static int gswip_get_sset_count(struct dsa_switch *ds, int port, int sset)
{
if (sset != ETH_SS_STATS)
return 0 ;
return ARRAY_SIZE(gswip_rmon_cnt);
}
static const struct phylink_mac_ops gswip_phylink_mac_ops = {
.mac_config = gswip_phylink_mac_config,
.mac_link_down = gswip_phylink_mac_link_down,
.mac_link_up = gswip_phylink_mac_link_up,
};
static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
.get_tag_protocol = gswip_get_tag_protocol,
.setup = gswip_setup,
.port_setup = gswip_port_setup,
.port_enable = gswip_port_enable,
.port_disable = gswip_port_disable,
.port_bridge_join = gswip_port_bridge_join,
.port_bridge_leave = gswip_port_bridge_leave,
.port_fast_age = gswip_port_fast_age,
.port_vlan_filtering = gswip_port_vlan_filtering,
.port_vlan_add = gswip_port_vlan_add,
.port_vlan_del = gswip_port_vlan_del,
.port_stp_state_set = gswip_port_stp_state_set,
.port_fdb_add = gswip_port_fdb_add,
.port_fdb_del = gswip_port_fdb_del,
.port_fdb_dump = gswip_port_fdb_dump,
.port_change_mtu = gswip_port_change_mtu,
.port_max_mtu = gswip_port_max_mtu,
.phylink_get_caps = gswip_xrx200_phylink_get_caps,
.get_strings = gswip_get_strings,
.get_ethtool_stats = gswip_get_ethtool_stats,
.get_sset_count = gswip_get_sset_count,
};
static const struct dsa_switch_ops gswip_xrx300_switch_ops = {
.get_tag_protocol = gswip_get_tag_protocol,
.setup = gswip_setup,
.port_enable = gswip_port_enable,
.port_disable = gswip_port_disable,
.port_bridge_join = gswip_port_bridge_join,
.port_bridge_leave = gswip_port_bridge_leave,
.port_fast_age = gswip_port_fast_age,
.port_vlan_filtering = gswip_port_vlan_filtering,
.port_vlan_add = gswip_port_vlan_add,
.port_vlan_del = gswip_port_vlan_del,
.port_stp_state_set = gswip_port_stp_state_set,
.port_fdb_add = gswip_port_fdb_add,
.port_fdb_del = gswip_port_fdb_del,
.port_fdb_dump = gswip_port_fdb_dump,
.port_change_mtu = gswip_port_change_mtu,
.port_max_mtu = gswip_port_max_mtu,
.phylink_get_caps = gswip_xrx300_phylink_get_caps,
.get_strings = gswip_get_strings,
.get_ethtool_stats = gswip_get_ethtool_stats,
.get_sset_count = gswip_get_sset_count,
};
static const struct xway_gphy_match_data xrx200a1x_gphy_data = {
.fe_firmware_name = "lantiq/xrx200_phy22f_a14.bin" ,
.ge_firmware_name = "lantiq/xrx200_phy11g_a14.bin" ,
};
static const struct xway_gphy_match_data xrx200a2x_gphy_data = {
.fe_firmware_name = "lantiq/xrx200_phy22f_a22.bin" ,
.ge_firmware_name = "lantiq/xrx200_phy11g_a22.bin" ,
};
static const struct xway_gphy_match_data xrx300_gphy_data = {
.fe_firmware_name = "lantiq/xrx300_phy22f_a21.bin" ,
.ge_firmware_name = "lantiq/xrx300_phy11g_a21.bin" ,
};
static const struct of_device_id xway_gphy_match[] __maybe_unused = {
{ .compatible = "lantiq,xrx200-gphy-fw" , .data = NULL },
{ .compatible = "lantiq,xrx200a1x-gphy-fw" , .data = &xrx200a1x_gphy_data },
{ .compatible = "lantiq,xrx200a2x-gphy-fw" , .data = &xrx200a2x_gphy_data },
{ .compatible = "lantiq,xrx300-gphy-fw" , .data = &xrx300_gphy_data },
{ .compatible = "lantiq,xrx330-gphy-fw" , .data = &xrx300_gphy_data },
{},
};
static int gswip_gphy_fw_load(struct gswip_priv *priv, struct gswip_gphy_fw *gphy_fw)
{
struct device *dev = priv->dev;
const struct firmware *fw;
void *fw_addr;
dma_addr_t dma_addr;
dma_addr_t dev_addr;
size_t size;
int ret;
ret = clk_prepare_enable(gphy_fw->clk_gate);
if (ret)
return ret;
reset_control_assert(gphy_fw->reset);
/* The vendor BSP uses a 200ms delay after asserting the reset line.
* Without this some users are observing that the PHY is not coming up
* on the MDIO bus .
*/
msleep(200 );
ret = request_firmware(&fw, gphy_fw->fw_name, dev);
if (ret)
return dev_err_probe(dev, ret, "failed to load firmware: %s\n" ,
gphy_fw->fw_name);
/* GPHY cores need the firmware code in a persistent and contiguous
* memory area with a 16 kB boundary aligned start address .
*/
size = fw->size + XRX200_GPHY_FW_ALIGN;
fw_addr = dmam_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
if (fw_addr) {
fw_addr = PTR_ALIGN(fw_addr, XRX200_GPHY_FW_ALIGN);
dev_addr = ALIGN(dma_addr, XRX200_GPHY_FW_ALIGN);
memcpy(fw_addr, fw->data, fw->size);
} else {
release_firmware(fw);
return dev_err_probe(dev, -ENOMEM,
"failed to alloc firmware memory\n" );
}
release_firmware(fw);
ret = regmap_write(priv->rcu_regmap, gphy_fw->fw_addr_offset, dev_addr);
if (ret)
return ret;
reset_control_deassert(gphy_fw->reset);
return ret;
}
static int gswip_gphy_fw_probe(struct gswip_priv *priv,
struct gswip_gphy_fw *gphy_fw,
struct device_node *gphy_fw_np, int i)
{
struct device *dev = priv->dev;
u32 gphy_mode;
int ret;
char gphyname[10 ];
snprintf(gphyname, sizeof (gphyname), "gphy%d" , i);
gphy_fw->clk_gate = devm_clk_get(dev, gphyname);
if (IS_ERR(gphy_fw->clk_gate)) {
return dev_err_probe(dev, PTR_ERR(gphy_fw->clk_gate),
"Failed to lookup gate clock\n" );
}
ret = of_property_read_u32(gphy_fw_np, "reg" , &gphy_fw->fw_addr_offset);
if (ret)
return ret;
ret = of_property_read_u32(gphy_fw_np, "lantiq,gphy-mode" , &gphy_mode);
/* Default to GE mode */
if (ret)
gphy_mode = GPHY_MODE_GE;
switch (gphy_mode) {
case GPHY_MODE_FE:
gphy_fw->fw_name = priv->gphy_fw_name_cfg->fe_firmware_name;
break ;
case GPHY_MODE_GE:
gphy_fw->fw_name = priv->gphy_fw_name_cfg->ge_firmware_name;
break ;
default :
return dev_err_probe(dev, -EINVAL, "Unknown GPHY mode %d\n" ,
gphy_mode);
}
gphy_fw->reset = of_reset_control_array_get_exclusive(gphy_fw_np);
if (IS_ERR(gphy_fw->reset))
return dev_err_probe(dev, PTR_ERR(gphy_fw->reset),
"Failed to lookup gphy reset\n" );
return gswip_gphy_fw_load(priv, gphy_fw);
}
static void gswip_gphy_fw_remove(struct gswip_priv *priv,
struct gswip_gphy_fw *gphy_fw)
{
int ret;
/* check if the device was fully probed */
if (!gphy_fw->fw_name)
return ;
ret = regmap_write(priv->rcu_regmap, gphy_fw->fw_addr_offset, 0 );
if (ret)
dev_err(priv->dev, "can not reset GPHY FW pointer\n" );
clk_disable_unprepare(gphy_fw->clk_gate);
reset_control_put(gphy_fw->reset);
}
static int gswip_gphy_fw_list(struct gswip_priv *priv,
struct device_node *gphy_fw_list_np, u32 version)
{
struct device *dev = priv->dev;
struct device_node *gphy_fw_np;
const struct of_device_id *match;
int err;
int i = 0 ;
/* The VRX200 rev 1.1 uses the GSWIP 2.0 and needs the older
* GPHY firmware . The VRX200 rev 1 . 2 uses the GSWIP 2 . 1 and also
* needs a different GPHY firmware .
*/
if (of_device_is_compatible(gphy_fw_list_np, "lantiq,xrx200-gphy-fw" )) {
switch (version) {
case GSWIP_VERSION_2_0:
priv->gphy_fw_name_cfg = &xrx200a1x_gphy_data;
break ;
case GSWIP_VERSION_2_1:
priv->gphy_fw_name_cfg = &xrx200a2x_gphy_data;
break ;
default :
return dev_err_probe(dev, -ENOENT,
"unknown GSWIP version: 0x%x\n" ,
version);
}
}
match = of_match_node(xway_gphy_match, gphy_fw_list_np);
if (match && match->data)
priv->gphy_fw_name_cfg = match->data;
if (!priv->gphy_fw_name_cfg)
return dev_err_probe(dev, -ENOENT,
"GPHY compatible type not supported\n" );
priv->num_gphy_fw = of_get_available_child_count(gphy_fw_list_np);
if (!priv->num_gphy_fw)
return -ENOENT;
priv->rcu_regmap = syscon_regmap_lookup_by_phandle(gphy_fw_list_np,
"lantiq,rcu" );
if (IS_ERR(priv->rcu_regmap))
return PTR_ERR(priv->rcu_regmap);
priv->gphy_fw = devm_kmalloc_array(dev, priv->num_gphy_fw,
sizeof (*priv->gphy_fw),
GFP_KERNEL | __GFP_ZERO);
if (!priv->gphy_fw)
return -ENOMEM;
for_each_available_child_of_node(gphy_fw_list_np, gphy_fw_np) {
err = gswip_gphy_fw_probe(priv, &priv->gphy_fw[i],
gphy_fw_np, i);
if (err) {
of_node_put(gphy_fw_np);
goto remove_gphy;
}
i++;
}
/* The standalone PHY11G requires 300ms to be fully
* initialized and ready for any MDIO communication after being
* taken out of reset . For the SoC - internal GPHY variant there
* is no ( known ) documentation for the minimum time after a
* reset . Use the same value as for the standalone variant as
* some users have reported internal PHYs not being detected
* without any delay .
*/
msleep(300 );
return 0 ;
remove_gphy:
for (i = 0 ; i < priv->num_gphy_fw; i++)
gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]);
return err;
}
static int gswip_probe(struct platform_device *pdev)
{
struct device_node *np, *gphy_fw_np;
struct device *dev = &pdev->dev;
struct gswip_priv *priv;
int err;
int i;
u32 version;
priv = devm_kzalloc(dev, sizeof (*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->gswip = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(priv->gswip))
return PTR_ERR(priv->gswip);
priv->mdio = devm_platform_ioremap_resource(pdev, 1 );
if (IS_ERR(priv->mdio))
return PTR_ERR(priv->mdio);
priv->mii = devm_platform_ioremap_resource(pdev, 2 );
if (IS_ERR(priv->mii))
return PTR_ERR(priv->mii);
priv->hw_info = of_device_get_match_data(dev);
if (!priv->hw_info)
return -EINVAL;
priv->ds = devm_kzalloc(dev, sizeof (*priv->ds), GFP_KERNEL);
if (!priv->ds)
return -ENOMEM;
priv->ds->dev = dev;
priv->ds->num_ports = priv->hw_info->max_ports;
priv->ds->priv = priv;
priv->ds->ops = priv->hw_info->ops;
priv->ds->phylink_mac_ops = &gswip_phylink_mac_ops;
priv->dev = dev;
mutex_init(&priv->pce_table_lock);
version = gswip_switch_r(priv, GSWIP_VERSION);
np = dev->of_node;
switch (version) {
case GSWIP_VERSION_2_0:
case GSWIP_VERSION_2_1:
if (!of_device_is_compatible(np, "lantiq,xrx200-gswip" ))
return -EINVAL;
break ;
case GSWIP_VERSION_2_2:
case GSWIP_VERSION_2_2_ETC:
if (!of_device_is_compatible(np, "lantiq,xrx300-gswip" ) &&
!of_device_is_compatible(np, "lantiq,xrx330-gswip" ))
return -EINVAL;
break ;
default :
return dev_err_probe(dev, -ENOENT,
"unknown GSWIP version: 0x%x\n" , version);
}
/* bring up the mdio bus */
gphy_fw_np = of_get_compatible_child(dev->of_node, "lantiq,gphy-fw" );
if (gphy_fw_np) {
err = gswip_gphy_fw_list(priv, gphy_fw_np, version);
of_node_put(gphy_fw_np);
if (err)
return dev_err_probe(dev, err,
"gphy fw probe failed\n" );
}
/* bring up the mdio bus */
err = gswip_mdio(priv);
if (err) {
dev_err_probe(dev, err, "mdio probe failed\n" );
goto gphy_fw_remove;
}
err = dsa_register_switch(priv->ds);
if (err) {
dev_err_probe(dev, err, "dsa switch registration failed\n" );
goto gphy_fw_remove;
}
if (!dsa_is_cpu_port(priv->ds, priv->hw_info->cpu_port)) {
err = dev_err_probe(dev, -EINVAL,
"wrong CPU port defined, HW only supports port: %i\n" ,
priv->hw_info->cpu_port);
goto disable_switch;
}
platform_set_drvdata(pdev, priv);
dev_info(dev, "probed GSWIP version %lx mod %lx\n" ,
(version & GSWIP_VERSION_REV_MASK) >> GSWIP_VERSION_REV_SHIFT,
(version & GSWIP_VERSION_MOD_MASK) >> GSWIP_VERSION_MOD_SHIFT);
return 0 ;
disable_switch:
gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0 , GSWIP_MDIO_GLOB);
dsa_unregister_switch(priv->ds);
gphy_fw_remove:
for (i = 0 ; i < priv->num_gphy_fw; i++)
gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]);
return err;
}
static void gswip_remove(struct platform_device *pdev)
{
struct gswip_priv *priv = platform_get_drvdata(pdev);
int i;
if (!priv)
return ;
/* disable the switch */
gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0 , GSWIP_MDIO_GLOB);
dsa_unregister_switch(priv->ds);
for (i = 0 ; i < priv->num_gphy_fw; i++)
gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]);
}
static void gswip_shutdown(struct platform_device *pdev)
{
struct gswip_priv *priv = platform_get_drvdata(pdev);
if (!priv)
return ;
dsa_switch_shutdown(priv->ds);
platform_set_drvdata(pdev, NULL);
}
static const struct gswip_hw_info gswip_xrx200 = {
.max_ports = 7 ,
.cpu_port = 6 ,
.ops = &gswip_xrx200_switch_ops,
};
static const struct gswip_hw_info gswip_xrx300 = {
.max_ports = 7 ,
.cpu_port = 6 ,
.ops = &gswip_xrx300_switch_ops,
};
static const struct of_device_id gswip_of_match[] = {
{ .compatible = "lantiq,xrx200-gswip" , .data = &gswip_xrx200 },
{ .compatible = "lantiq,xrx300-gswip" , .data = &gswip_xrx300 },
{ .compatible = "lantiq,xrx330-gswip" , .data = &gswip_xrx300 },
{},
};
MODULE_DEVICE_TABLE(of, gswip_of_match);
static struct platform_driver gswip_driver = {
.probe = gswip_probe,
.remove = gswip_remove,
.shutdown = gswip_shutdown,
.driver = {
.name = "gswip" ,
.of_match_table = gswip_of_match,
},
};
module_platform_driver(gswip_driver);
MODULE_FIRMWARE("lantiq/xrx300_phy11g_a21.bin" );
MODULE_FIRMWARE("lantiq/xrx300_phy22f_a21.bin" );
MODULE_FIRMWARE("lantiq/xrx200_phy11g_a14.bin" );
MODULE_FIRMWARE("lantiq/xrx200_phy11g_a22.bin" );
MODULE_FIRMWARE("lantiq/xrx200_phy22f_a14.bin" );
MODULE_FIRMWARE("lantiq/xrx200_phy22f_a22.bin" );
MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>" );
MODULE_DESCRIPTION("Lantiq / Intel GSWIP driver" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=96 H=95 G=95
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.31Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27)
¤
*Eine klare Vorstellung vom Zielzustand