The current assumption is that the felix DSA driver has flooding knobs per traffic class, while ocelot switchdev has a single flooding knob. This was correct for felix VSC9959 and ocelot VSC7514, but with the introduction of seville VSC9953, we see a switch driven by felix.c which has a single flooding knob. So it is clear that we must do what should have been done from the beginning, which is not to overwrite the configuration done by ocelot.c in felix, but instead to teach the common ocelot library about the differences in our switches, and set up the flooding PGIDs centrally. The effect that the bogus iteration through FELIX_NUM_TC has upon seville is quite dramatic. ANA_FLOODING is located at 0x00b548, and ANA_FLOODING_IPMC is located at 0x00b54c. So the bogus iteration will actually overwrite ANA_FLOODING_IPMC when attempting to write ANA_FLOODING[1]. There is no ANA_FLOODING[1] in sevile, just ANA_FLOODING. And when ANA_FLOODING_IPMC is overwritten with a bogus value, the effect is that ANA_FLOODING_IPMC gets the value of 0x0003CF7D: MC6_DATA = 61, MC6_CTRL = 61, MC4_DATA = 60, MC4_CTRL = 0. Because MC4_CTRL is zero, this means that IPv4 multicast control packets are not flooded, but dropped. An invalid configuration, and this is how the issue was actually spotted. Reported-by: Eldar Gasanov <eldargasanov2@gmail.com> Reported-by: Maxim Kochetkov <fido_max@inbox.ru> Tested-by: Eldar Gasanov <eldargasanov2@gmail.com> Fixes: 84705fc16552 ("net: dsa: felix: introduce support for Seville VSC9953 switch") Fixes: 3c7b51bd39b2 ("net: dsa: felix: allow flooding for all traffic classes") Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/r/20201204175416.1445937-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1518 lines
46 KiB
C
1518 lines
46 KiB
C
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
/* Copyright 2017 Microsemi Corporation
|
|
* Copyright 2018-2019 NXP Semiconductors
|
|
*/
|
|
#include <linux/fsl/enetc_mdio.h>
|
|
#include <soc/mscc/ocelot_qsys.h>
|
|
#include <soc/mscc/ocelot_vcap.h>
|
|
#include <soc/mscc/ocelot_ptp.h>
|
|
#include <soc/mscc/ocelot_sys.h>
|
|
#include <soc/mscc/ocelot.h>
|
|
#include <linux/packing.h>
|
|
#include <linux/pcs-lynx.h>
|
|
#include <net/pkt_sched.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/mdio.h>
|
|
#include <linux/pci.h>
|
|
#include "felix.h"
|
|
|
|
#define VSC9959_TAS_GCL_ENTRY_MAX 63
|
|
|
|
static const u32 vsc9959_ana_regmap[] = {
|
|
REG(ANA_ADVLEARN, 0x0089a0),
|
|
REG(ANA_VLANMASK, 0x0089a4),
|
|
REG_RESERVED(ANA_PORT_B_DOMAIN),
|
|
REG(ANA_ANAGEFIL, 0x0089ac),
|
|
REG(ANA_ANEVENTS, 0x0089b0),
|
|
REG(ANA_STORMLIMIT_BURST, 0x0089b4),
|
|
REG(ANA_STORMLIMIT_CFG, 0x0089b8),
|
|
REG(ANA_ISOLATED_PORTS, 0x0089c8),
|
|
REG(ANA_COMMUNITY_PORTS, 0x0089cc),
|
|
REG(ANA_AUTOAGE, 0x0089d0),
|
|
REG(ANA_MACTOPTIONS, 0x0089d4),
|
|
REG(ANA_LEARNDISC, 0x0089d8),
|
|
REG(ANA_AGENCTRL, 0x0089dc),
|
|
REG(ANA_MIRRORPORTS, 0x0089e0),
|
|
REG(ANA_EMIRRORPORTS, 0x0089e4),
|
|
REG(ANA_FLOODING, 0x0089e8),
|
|
REG(ANA_FLOODING_IPMC, 0x008a08),
|
|
REG(ANA_SFLOW_CFG, 0x008a0c),
|
|
REG(ANA_PORT_MODE, 0x008a28),
|
|
REG(ANA_CUT_THRU_CFG, 0x008a48),
|
|
REG(ANA_PGID_PGID, 0x008400),
|
|
REG(ANA_TABLES_ANMOVED, 0x007f1c),
|
|
REG(ANA_TABLES_MACHDATA, 0x007f20),
|
|
REG(ANA_TABLES_MACLDATA, 0x007f24),
|
|
REG(ANA_TABLES_STREAMDATA, 0x007f28),
|
|
REG(ANA_TABLES_MACACCESS, 0x007f2c),
|
|
REG(ANA_TABLES_MACTINDX, 0x007f30),
|
|
REG(ANA_TABLES_VLANACCESS, 0x007f34),
|
|
REG(ANA_TABLES_VLANTIDX, 0x007f38),
|
|
REG(ANA_TABLES_ISDXACCESS, 0x007f3c),
|
|
REG(ANA_TABLES_ISDXTIDX, 0x007f40),
|
|
REG(ANA_TABLES_ENTRYLIM, 0x007f00),
|
|
REG(ANA_TABLES_PTP_ID_HIGH, 0x007f44),
|
|
REG(ANA_TABLES_PTP_ID_LOW, 0x007f48),
|
|
REG(ANA_TABLES_STREAMACCESS, 0x007f4c),
|
|
REG(ANA_TABLES_STREAMTIDX, 0x007f50),
|
|
REG(ANA_TABLES_SEQ_HISTORY, 0x007f54),
|
|
REG(ANA_TABLES_SEQ_MASK, 0x007f58),
|
|
REG(ANA_TABLES_SFID_MASK, 0x007f5c),
|
|
REG(ANA_TABLES_SFIDACCESS, 0x007f60),
|
|
REG(ANA_TABLES_SFIDTIDX, 0x007f64),
|
|
REG(ANA_MSTI_STATE, 0x008600),
|
|
REG(ANA_OAM_UPM_LM_CNT, 0x008000),
|
|
REG(ANA_SG_ACCESS_CTRL, 0x008a64),
|
|
REG(ANA_SG_CONFIG_REG_1, 0x007fb0),
|
|
REG(ANA_SG_CONFIG_REG_2, 0x007fb4),
|
|
REG(ANA_SG_CONFIG_REG_3, 0x007fb8),
|
|
REG(ANA_SG_CONFIG_REG_4, 0x007fbc),
|
|
REG(ANA_SG_CONFIG_REG_5, 0x007fc0),
|
|
REG(ANA_SG_GCL_GS_CONFIG, 0x007f80),
|
|
REG(ANA_SG_GCL_TI_CONFIG, 0x007f90),
|
|
REG(ANA_SG_STATUS_REG_1, 0x008980),
|
|
REG(ANA_SG_STATUS_REG_2, 0x008984),
|
|
REG(ANA_SG_STATUS_REG_3, 0x008988),
|
|
REG(ANA_PORT_VLAN_CFG, 0x007800),
|
|
REG(ANA_PORT_DROP_CFG, 0x007804),
|
|
REG(ANA_PORT_QOS_CFG, 0x007808),
|
|
REG(ANA_PORT_VCAP_CFG, 0x00780c),
|
|
REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007810),
|
|
REG(ANA_PORT_VCAP_S2_CFG, 0x00781c),
|
|
REG(ANA_PORT_PCP_DEI_MAP, 0x007820),
|
|
REG(ANA_PORT_CPU_FWD_CFG, 0x007860),
|
|
REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007864),
|
|
REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007868),
|
|
REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00786c),
|
|
REG(ANA_PORT_PORT_CFG, 0x007870),
|
|
REG(ANA_PORT_POL_CFG, 0x007874),
|
|
REG(ANA_PORT_PTP_CFG, 0x007878),
|
|
REG(ANA_PORT_PTP_DLY1_CFG, 0x00787c),
|
|
REG(ANA_PORT_PTP_DLY2_CFG, 0x007880),
|
|
REG(ANA_PORT_SFID_CFG, 0x007884),
|
|
REG(ANA_PFC_PFC_CFG, 0x008800),
|
|
REG_RESERVED(ANA_PFC_PFC_TIMER),
|
|
REG_RESERVED(ANA_IPT_OAM_MEP_CFG),
|
|
REG_RESERVED(ANA_IPT_IPT),
|
|
REG_RESERVED(ANA_PPT_PPT),
|
|
REG_RESERVED(ANA_FID_MAP_FID_MAP),
|
|
REG(ANA_AGGR_CFG, 0x008a68),
|
|
REG(ANA_CPUQ_CFG, 0x008a6c),
|
|
REG_RESERVED(ANA_CPUQ_CFG2),
|
|
REG(ANA_CPUQ_8021_CFG, 0x008a74),
|
|
REG(ANA_DSCP_CFG, 0x008ab4),
|
|
REG(ANA_DSCP_REWR_CFG, 0x008bb4),
|
|
REG(ANA_VCAP_RNG_TYPE_CFG, 0x008bf4),
|
|
REG(ANA_VCAP_RNG_VAL_CFG, 0x008c14),
|
|
REG_RESERVED(ANA_VRAP_CFG),
|
|
REG_RESERVED(ANA_VRAP_HDR_DATA),
|
|
REG_RESERVED(ANA_VRAP_HDR_MASK),
|
|
REG(ANA_DISCARD_CFG, 0x008c40),
|
|
REG(ANA_FID_CFG, 0x008c44),
|
|
REG(ANA_POL_PIR_CFG, 0x004000),
|
|
REG(ANA_POL_CIR_CFG, 0x004004),
|
|
REG(ANA_POL_MODE_CFG, 0x004008),
|
|
REG(ANA_POL_PIR_STATE, 0x00400c),
|
|
REG(ANA_POL_CIR_STATE, 0x004010),
|
|
REG_RESERVED(ANA_POL_STATE),
|
|
REG(ANA_POL_FLOWC, 0x008c48),
|
|
REG(ANA_POL_HYST, 0x008cb4),
|
|
REG_RESERVED(ANA_POL_MISC_CFG),
|
|
};
|
|
|
|
static const u32 vsc9959_qs_regmap[] = {
|
|
REG(QS_XTR_GRP_CFG, 0x000000),
|
|
REG(QS_XTR_RD, 0x000008),
|
|
REG(QS_XTR_FRM_PRUNING, 0x000010),
|
|
REG(QS_XTR_FLUSH, 0x000018),
|
|
REG(QS_XTR_DATA_PRESENT, 0x00001c),
|
|
REG(QS_XTR_CFG, 0x000020),
|
|
REG(QS_INJ_GRP_CFG, 0x000024),
|
|
REG(QS_INJ_WR, 0x00002c),
|
|
REG(QS_INJ_CTRL, 0x000034),
|
|
REG(QS_INJ_STATUS, 0x00003c),
|
|
REG(QS_INJ_ERR, 0x000040),
|
|
REG_RESERVED(QS_INH_DBG),
|
|
};
|
|
|
|
static const u32 vsc9959_vcap_regmap[] = {
|
|
/* VCAP_CORE_CFG */
|
|
REG(VCAP_CORE_UPDATE_CTRL, 0x000000),
|
|
REG(VCAP_CORE_MV_CFG, 0x000004),
|
|
/* VCAP_CORE_CACHE */
|
|
REG(VCAP_CACHE_ENTRY_DAT, 0x000008),
|
|
REG(VCAP_CACHE_MASK_DAT, 0x000108),
|
|
REG(VCAP_CACHE_ACTION_DAT, 0x000208),
|
|
REG(VCAP_CACHE_CNT_DAT, 0x000308),
|
|
REG(VCAP_CACHE_TG_DAT, 0x000388),
|
|
/* VCAP_CONST */
|
|
REG(VCAP_CONST_VCAP_VER, 0x000398),
|
|
REG(VCAP_CONST_ENTRY_WIDTH, 0x00039c),
|
|
REG(VCAP_CONST_ENTRY_CNT, 0x0003a0),
|
|
REG(VCAP_CONST_ENTRY_SWCNT, 0x0003a4),
|
|
REG(VCAP_CONST_ENTRY_TG_WIDTH, 0x0003a8),
|
|
REG(VCAP_CONST_ACTION_DEF_CNT, 0x0003ac),
|
|
REG(VCAP_CONST_ACTION_WIDTH, 0x0003b0),
|
|
REG(VCAP_CONST_CNT_WIDTH, 0x0003b4),
|
|
REG(VCAP_CONST_CORE_CNT, 0x0003b8),
|
|
REG(VCAP_CONST_IF_CNT, 0x0003bc),
|
|
};
|
|
|
|
static const u32 vsc9959_qsys_regmap[] = {
|
|
REG(QSYS_PORT_MODE, 0x00f460),
|
|
REG(QSYS_SWITCH_PORT_MODE, 0x00f480),
|
|
REG(QSYS_STAT_CNT_CFG, 0x00f49c),
|
|
REG(QSYS_EEE_CFG, 0x00f4a0),
|
|
REG(QSYS_EEE_THRES, 0x00f4b8),
|
|
REG(QSYS_IGR_NO_SHARING, 0x00f4bc),
|
|
REG(QSYS_EGR_NO_SHARING, 0x00f4c0),
|
|
REG(QSYS_SW_STATUS, 0x00f4c4),
|
|
REG(QSYS_EXT_CPU_CFG, 0x00f4e0),
|
|
REG_RESERVED(QSYS_PAD_CFG),
|
|
REG(QSYS_CPU_GROUP_MAP, 0x00f4e8),
|
|
REG_RESERVED(QSYS_QMAP),
|
|
REG_RESERVED(QSYS_ISDX_SGRP),
|
|
REG_RESERVED(QSYS_TIMED_FRAME_ENTRY),
|
|
REG(QSYS_TFRM_MISC, 0x00f50c),
|
|
REG(QSYS_TFRM_PORT_DLY, 0x00f510),
|
|
REG(QSYS_TFRM_TIMER_CFG_1, 0x00f514),
|
|
REG(QSYS_TFRM_TIMER_CFG_2, 0x00f518),
|
|
REG(QSYS_TFRM_TIMER_CFG_3, 0x00f51c),
|
|
REG(QSYS_TFRM_TIMER_CFG_4, 0x00f520),
|
|
REG(QSYS_TFRM_TIMER_CFG_5, 0x00f524),
|
|
REG(QSYS_TFRM_TIMER_CFG_6, 0x00f528),
|
|
REG(QSYS_TFRM_TIMER_CFG_7, 0x00f52c),
|
|
REG(QSYS_TFRM_TIMER_CFG_8, 0x00f530),
|
|
REG(QSYS_RED_PROFILE, 0x00f534),
|
|
REG(QSYS_RES_QOS_MODE, 0x00f574),
|
|
REG(QSYS_RES_CFG, 0x00c000),
|
|
REG(QSYS_RES_STAT, 0x00c004),
|
|
REG(QSYS_EGR_DROP_MODE, 0x00f578),
|
|
REG(QSYS_EQ_CTRL, 0x00f57c),
|
|
REG_RESERVED(QSYS_EVENTS_CORE),
|
|
REG(QSYS_QMAXSDU_CFG_0, 0x00f584),
|
|
REG(QSYS_QMAXSDU_CFG_1, 0x00f5a0),
|
|
REG(QSYS_QMAXSDU_CFG_2, 0x00f5bc),
|
|
REG(QSYS_QMAXSDU_CFG_3, 0x00f5d8),
|
|
REG(QSYS_QMAXSDU_CFG_4, 0x00f5f4),
|
|
REG(QSYS_QMAXSDU_CFG_5, 0x00f610),
|
|
REG(QSYS_QMAXSDU_CFG_6, 0x00f62c),
|
|
REG(QSYS_QMAXSDU_CFG_7, 0x00f648),
|
|
REG(QSYS_PREEMPTION_CFG, 0x00f664),
|
|
REG(QSYS_CIR_CFG, 0x000000),
|
|
REG(QSYS_EIR_CFG, 0x000004),
|
|
REG(QSYS_SE_CFG, 0x000008),
|
|
REG(QSYS_SE_DWRR_CFG, 0x00000c),
|
|
REG_RESERVED(QSYS_SE_CONNECT),
|
|
REG(QSYS_SE_DLB_SENSE, 0x000040),
|
|
REG(QSYS_CIR_STATE, 0x000044),
|
|
REG(QSYS_EIR_STATE, 0x000048),
|
|
REG_RESERVED(QSYS_SE_STATE),
|
|
REG(QSYS_HSCH_MISC_CFG, 0x00f67c),
|
|
REG(QSYS_TAG_CONFIG, 0x00f680),
|
|
REG(QSYS_TAS_PARAM_CFG_CTRL, 0x00f698),
|
|
REG(QSYS_PORT_MAX_SDU, 0x00f69c),
|
|
REG(QSYS_PARAM_CFG_REG_1, 0x00f440),
|
|
REG(QSYS_PARAM_CFG_REG_2, 0x00f444),
|
|
REG(QSYS_PARAM_CFG_REG_3, 0x00f448),
|
|
REG(QSYS_PARAM_CFG_REG_4, 0x00f44c),
|
|
REG(QSYS_PARAM_CFG_REG_5, 0x00f450),
|
|
REG(QSYS_GCL_CFG_REG_1, 0x00f454),
|
|
REG(QSYS_GCL_CFG_REG_2, 0x00f458),
|
|
REG(QSYS_PARAM_STATUS_REG_1, 0x00f400),
|
|
REG(QSYS_PARAM_STATUS_REG_2, 0x00f404),
|
|
REG(QSYS_PARAM_STATUS_REG_3, 0x00f408),
|
|
REG(QSYS_PARAM_STATUS_REG_4, 0x00f40c),
|
|
REG(QSYS_PARAM_STATUS_REG_5, 0x00f410),
|
|
REG(QSYS_PARAM_STATUS_REG_6, 0x00f414),
|
|
REG(QSYS_PARAM_STATUS_REG_7, 0x00f418),
|
|
REG(QSYS_PARAM_STATUS_REG_8, 0x00f41c),
|
|
REG(QSYS_PARAM_STATUS_REG_9, 0x00f420),
|
|
REG(QSYS_GCL_STATUS_REG_1, 0x00f424),
|
|
REG(QSYS_GCL_STATUS_REG_2, 0x00f428),
|
|
};
|
|
|
|
static const u32 vsc9959_rew_regmap[] = {
|
|
REG(REW_PORT_VLAN_CFG, 0x000000),
|
|
REG(REW_TAG_CFG, 0x000004),
|
|
REG(REW_PORT_CFG, 0x000008),
|
|
REG(REW_DSCP_CFG, 0x00000c),
|
|
REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010),
|
|
REG(REW_PTP_CFG, 0x000050),
|
|
REG(REW_PTP_DLY1_CFG, 0x000054),
|
|
REG(REW_RED_TAG_CFG, 0x000058),
|
|
REG(REW_DSCP_REMAP_DP1_CFG, 0x000410),
|
|
REG(REW_DSCP_REMAP_CFG, 0x000510),
|
|
REG_RESERVED(REW_STAT_CFG),
|
|
REG_RESERVED(REW_REW_STICKY),
|
|
REG_RESERVED(REW_PPT),
|
|
};
|
|
|
|
static const u32 vsc9959_sys_regmap[] = {
|
|
REG(SYS_COUNT_RX_OCTETS, 0x000000),
|
|
REG(SYS_COUNT_RX_MULTICAST, 0x000008),
|
|
REG(SYS_COUNT_RX_SHORTS, 0x000010),
|
|
REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
|
|
REG(SYS_COUNT_RX_JABBERS, 0x000018),
|
|
REG(SYS_COUNT_RX_64, 0x000024),
|
|
REG(SYS_COUNT_RX_65_127, 0x000028),
|
|
REG(SYS_COUNT_RX_128_255, 0x00002c),
|
|
REG(SYS_COUNT_RX_256_1023, 0x000030),
|
|
REG(SYS_COUNT_RX_1024_1526, 0x000034),
|
|
REG(SYS_COUNT_RX_1527_MAX, 0x000038),
|
|
REG(SYS_COUNT_RX_LONGS, 0x000044),
|
|
REG(SYS_COUNT_TX_OCTETS, 0x000200),
|
|
REG(SYS_COUNT_TX_COLLISION, 0x000210),
|
|
REG(SYS_COUNT_TX_DROPS, 0x000214),
|
|
REG(SYS_COUNT_TX_64, 0x00021c),
|
|
REG(SYS_COUNT_TX_65_127, 0x000220),
|
|
REG(SYS_COUNT_TX_128_511, 0x000224),
|
|
REG(SYS_COUNT_TX_512_1023, 0x000228),
|
|
REG(SYS_COUNT_TX_1024_1526, 0x00022c),
|
|
REG(SYS_COUNT_TX_1527_MAX, 0x000230),
|
|
REG(SYS_COUNT_TX_AGING, 0x000278),
|
|
REG(SYS_RESET_CFG, 0x000e00),
|
|
REG(SYS_SR_ETYPE_CFG, 0x000e04),
|
|
REG(SYS_VLAN_ETYPE_CFG, 0x000e08),
|
|
REG(SYS_PORT_MODE, 0x000e0c),
|
|
REG(SYS_FRONT_PORT_MODE, 0x000e2c),
|
|
REG(SYS_FRM_AGING, 0x000e44),
|
|
REG(SYS_STAT_CFG, 0x000e48),
|
|
REG(SYS_SW_STATUS, 0x000e4c),
|
|
REG_RESERVED(SYS_MISC_CFG),
|
|
REG(SYS_REW_MAC_HIGH_CFG, 0x000e6c),
|
|
REG(SYS_REW_MAC_LOW_CFG, 0x000e84),
|
|
REG(SYS_TIMESTAMP_OFFSET, 0x000e9c),
|
|
REG(SYS_PAUSE_CFG, 0x000ea0),
|
|
REG(SYS_PAUSE_TOT_CFG, 0x000ebc),
|
|
REG(SYS_ATOP, 0x000ec0),
|
|
REG(SYS_ATOP_TOT_CFG, 0x000edc),
|
|
REG(SYS_MAC_FC_CFG, 0x000ee0),
|
|
REG(SYS_MMGT, 0x000ef8),
|
|
REG_RESERVED(SYS_MMGT_FAST),
|
|
REG_RESERVED(SYS_EVENTS_DIF),
|
|
REG_RESERVED(SYS_EVENTS_CORE),
|
|
REG_RESERVED(SYS_CNT),
|
|
REG(SYS_PTP_STATUS, 0x000f14),
|
|
REG(SYS_PTP_TXSTAMP, 0x000f18),
|
|
REG(SYS_PTP_NXT, 0x000f1c),
|
|
REG(SYS_PTP_CFG, 0x000f20),
|
|
REG(SYS_RAM_INIT, 0x000f24),
|
|
REG_RESERVED(SYS_CM_ADDR),
|
|
REG_RESERVED(SYS_CM_DATA_WR),
|
|
REG_RESERVED(SYS_CM_DATA_RD),
|
|
REG_RESERVED(SYS_CM_OP),
|
|
REG_RESERVED(SYS_CM_DATA),
|
|
};
|
|
|
|
static const u32 vsc9959_ptp_regmap[] = {
|
|
REG(PTP_PIN_CFG, 0x000000),
|
|
REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
|
|
REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
|
|
REG(PTP_PIN_TOD_NSEC, 0x00000c),
|
|
REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014),
|
|
REG(PTP_PIN_WF_LOW_PERIOD, 0x000018),
|
|
REG(PTP_CFG_MISC, 0x0000a0),
|
|
REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
|
|
REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
|
|
};
|
|
|
|
static const u32 vsc9959_gcb_regmap[] = {
|
|
REG(GCB_SOFT_RST, 0x000004),
|
|
};
|
|
|
|
static const u32 vsc9959_dev_gmii_regmap[] = {
|
|
REG(DEV_CLOCK_CFG, 0x0),
|
|
REG(DEV_PORT_MISC, 0x4),
|
|
REG(DEV_EVENTS, 0x8),
|
|
REG(DEV_EEE_CFG, 0xc),
|
|
REG(DEV_RX_PATH_DELAY, 0x10),
|
|
REG(DEV_TX_PATH_DELAY, 0x14),
|
|
REG(DEV_PTP_PREDICT_CFG, 0x18),
|
|
REG(DEV_MAC_ENA_CFG, 0x1c),
|
|
REG(DEV_MAC_MODE_CFG, 0x20),
|
|
REG(DEV_MAC_MAXLEN_CFG, 0x24),
|
|
REG(DEV_MAC_TAGS_CFG, 0x28),
|
|
REG(DEV_MAC_ADV_CHK_CFG, 0x2c),
|
|
REG(DEV_MAC_IFG_CFG, 0x30),
|
|
REG(DEV_MAC_HDX_CFG, 0x34),
|
|
REG(DEV_MAC_DBG_CFG, 0x38),
|
|
REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c),
|
|
REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40),
|
|
REG(DEV_MAC_STICKY, 0x44),
|
|
REG_RESERVED(PCS1G_CFG),
|
|
REG_RESERVED(PCS1G_MODE_CFG),
|
|
REG_RESERVED(PCS1G_SD_CFG),
|
|
REG_RESERVED(PCS1G_ANEG_CFG),
|
|
REG_RESERVED(PCS1G_ANEG_NP_CFG),
|
|
REG_RESERVED(PCS1G_LB_CFG),
|
|
REG_RESERVED(PCS1G_DBG_CFG),
|
|
REG_RESERVED(PCS1G_CDET_CFG),
|
|
REG_RESERVED(PCS1G_ANEG_STATUS),
|
|
REG_RESERVED(PCS1G_ANEG_NP_STATUS),
|
|
REG_RESERVED(PCS1G_LINK_STATUS),
|
|
REG_RESERVED(PCS1G_LINK_DOWN_CNT),
|
|
REG_RESERVED(PCS1G_STICKY),
|
|
REG_RESERVED(PCS1G_DEBUG_STATUS),
|
|
REG_RESERVED(PCS1G_LPI_CFG),
|
|
REG_RESERVED(PCS1G_LPI_WAKE_ERROR_CNT),
|
|
REG_RESERVED(PCS1G_LPI_STATUS),
|
|
REG_RESERVED(PCS1G_TSTPAT_MODE_CFG),
|
|
REG_RESERVED(PCS1G_TSTPAT_STATUS),
|
|
REG_RESERVED(DEV_PCS_FX100_CFG),
|
|
REG_RESERVED(DEV_PCS_FX100_STATUS),
|
|
};
|
|
|
|
static const u32 *vsc9959_regmap[TARGET_MAX] = {
|
|
[ANA] = vsc9959_ana_regmap,
|
|
[QS] = vsc9959_qs_regmap,
|
|
[QSYS] = vsc9959_qsys_regmap,
|
|
[REW] = vsc9959_rew_regmap,
|
|
[SYS] = vsc9959_sys_regmap,
|
|
[S0] = vsc9959_vcap_regmap,
|
|
[S1] = vsc9959_vcap_regmap,
|
|
[S2] = vsc9959_vcap_regmap,
|
|
[PTP] = vsc9959_ptp_regmap,
|
|
[GCB] = vsc9959_gcb_regmap,
|
|
[DEV_GMII] = vsc9959_dev_gmii_regmap,
|
|
};
|
|
|
|
/* Addresses are relative to the PCI device's base address */
|
|
static const struct resource vsc9959_target_io_res[TARGET_MAX] = {
|
|
[ANA] = {
|
|
.start = 0x0280000,
|
|
.end = 0x028ffff,
|
|
.name = "ana",
|
|
},
|
|
[QS] = {
|
|
.start = 0x0080000,
|
|
.end = 0x00800ff,
|
|
.name = "qs",
|
|
},
|
|
[QSYS] = {
|
|
.start = 0x0200000,
|
|
.end = 0x021ffff,
|
|
.name = "qsys",
|
|
},
|
|
[REW] = {
|
|
.start = 0x0030000,
|
|
.end = 0x003ffff,
|
|
.name = "rew",
|
|
},
|
|
[SYS] = {
|
|
.start = 0x0010000,
|
|
.end = 0x001ffff,
|
|
.name = "sys",
|
|
},
|
|
[S0] = {
|
|
.start = 0x0040000,
|
|
.end = 0x00403ff,
|
|
.name = "s0",
|
|
},
|
|
[S1] = {
|
|
.start = 0x0050000,
|
|
.end = 0x00503ff,
|
|
.name = "s1",
|
|
},
|
|
[S2] = {
|
|
.start = 0x0060000,
|
|
.end = 0x00603ff,
|
|
.name = "s2",
|
|
},
|
|
[PTP] = {
|
|
.start = 0x0090000,
|
|
.end = 0x00900cb,
|
|
.name = "ptp",
|
|
},
|
|
[GCB] = {
|
|
.start = 0x0070000,
|
|
.end = 0x00701ff,
|
|
.name = "devcpu_gcb",
|
|
},
|
|
};
|
|
|
|
static const struct resource vsc9959_port_io_res[] = {
|
|
{
|
|
.start = 0x0100000,
|
|
.end = 0x010ffff,
|
|
.name = "port0",
|
|
},
|
|
{
|
|
.start = 0x0110000,
|
|
.end = 0x011ffff,
|
|
.name = "port1",
|
|
},
|
|
{
|
|
.start = 0x0120000,
|
|
.end = 0x012ffff,
|
|
.name = "port2",
|
|
},
|
|
{
|
|
.start = 0x0130000,
|
|
.end = 0x013ffff,
|
|
.name = "port3",
|
|
},
|
|
{
|
|
.start = 0x0140000,
|
|
.end = 0x014ffff,
|
|
.name = "port4",
|
|
},
|
|
{
|
|
.start = 0x0150000,
|
|
.end = 0x015ffff,
|
|
.name = "port5",
|
|
},
|
|
};
|
|
|
|
/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an
|
|
* SGMII/QSGMII MAC PCS can be found.
|
|
*/
|
|
static const struct resource vsc9959_imdio_res = {
|
|
.start = 0x8030,
|
|
.end = 0x8040,
|
|
.name = "imdio",
|
|
};
|
|
|
|
static const struct reg_field vsc9959_regfields[REGFIELD_MAX] = {
|
|
[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6),
|
|
[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5),
|
|
[ANA_ANEVENTS_FLOOD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 30, 30),
|
|
[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 26, 26),
|
|
[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 24, 24),
|
|
[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 23, 23),
|
|
[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 22, 22),
|
|
[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 21, 21),
|
|
[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 20, 20),
|
|
[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 19, 19),
|
|
[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
|
|
[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 17, 17),
|
|
[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 15, 15),
|
|
[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 14, 14),
|
|
[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 13, 13),
|
|
[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 12, 12),
|
|
[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
|
|
[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
|
|
[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 9, 9),
|
|
[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 8, 8),
|
|
[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 7, 7),
|
|
[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
|
|
[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
|
|
[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 4, 4),
|
|
[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 3, 3),
|
|
[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 2, 2),
|
|
[ANA_ANEVENTS_SEQ_GEN_ERR_0] = REG_FIELD(ANA_ANEVENTS, 1, 1),
|
|
[ANA_ANEVENTS_SEQ_GEN_ERR_1] = REG_FIELD(ANA_ANEVENTS, 0, 0),
|
|
[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 16, 16),
|
|
[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 11, 12),
|
|
[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 10),
|
|
[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 0, 0),
|
|
[GCB_SOFT_RST_SWC_RST] = REG_FIELD(GCB_SOFT_RST, 0, 0),
|
|
/* Replicated per number of ports (7), register size 4 per port */
|
|
[QSYS_SWITCH_PORT_MODE_PORT_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 14, 14, 7, 4),
|
|
[QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 11, 13, 7, 4),
|
|
[QSYS_SWITCH_PORT_MODE_YEL_RSRVD] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 10, 10, 7, 4),
|
|
[QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 9, 9, 7, 4),
|
|
[QSYS_SWITCH_PORT_MODE_TX_PFC_ENA] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 1, 8, 7, 4),
|
|
[QSYS_SWITCH_PORT_MODE_TX_PFC_MODE] = REG_FIELD_ID(QSYS_SWITCH_PORT_MODE, 0, 0, 7, 4),
|
|
[SYS_PORT_MODE_DATA_WO_TS] = REG_FIELD_ID(SYS_PORT_MODE, 5, 6, 7, 4),
|
|
[SYS_PORT_MODE_INCL_INJ_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 3, 4, 7, 4),
|
|
[SYS_PORT_MODE_INCL_XTR_HDR] = REG_FIELD_ID(SYS_PORT_MODE, 1, 2, 7, 4),
|
|
[SYS_PORT_MODE_INCL_HDR_ERR] = REG_FIELD_ID(SYS_PORT_MODE, 0, 0, 7, 4),
|
|
[SYS_PAUSE_CFG_PAUSE_START] = REG_FIELD_ID(SYS_PAUSE_CFG, 10, 18, 7, 4),
|
|
[SYS_PAUSE_CFG_PAUSE_STOP] = REG_FIELD_ID(SYS_PAUSE_CFG, 1, 9, 7, 4),
|
|
[SYS_PAUSE_CFG_PAUSE_ENA] = REG_FIELD_ID(SYS_PAUSE_CFG, 0, 1, 7, 4),
|
|
};
|
|
|
|
static const struct ocelot_stat_layout vsc9959_stats_layout[] = {
|
|
{ .offset = 0x00, .name = "rx_octets", },
|
|
{ .offset = 0x01, .name = "rx_unicast", },
|
|
{ .offset = 0x02, .name = "rx_multicast", },
|
|
{ .offset = 0x03, .name = "rx_broadcast", },
|
|
{ .offset = 0x04, .name = "rx_shorts", },
|
|
{ .offset = 0x05, .name = "rx_fragments", },
|
|
{ .offset = 0x06, .name = "rx_jabbers", },
|
|
{ .offset = 0x07, .name = "rx_crc_align_errs", },
|
|
{ .offset = 0x08, .name = "rx_sym_errs", },
|
|
{ .offset = 0x09, .name = "rx_frames_below_65_octets", },
|
|
{ .offset = 0x0A, .name = "rx_frames_65_to_127_octets", },
|
|
{ .offset = 0x0B, .name = "rx_frames_128_to_255_octets", },
|
|
{ .offset = 0x0C, .name = "rx_frames_256_to_511_octets", },
|
|
{ .offset = 0x0D, .name = "rx_frames_512_to_1023_octets", },
|
|
{ .offset = 0x0E, .name = "rx_frames_1024_to_1526_octets", },
|
|
{ .offset = 0x0F, .name = "rx_frames_over_1526_octets", },
|
|
{ .offset = 0x10, .name = "rx_pause", },
|
|
{ .offset = 0x11, .name = "rx_control", },
|
|
{ .offset = 0x12, .name = "rx_longs", },
|
|
{ .offset = 0x13, .name = "rx_classified_drops", },
|
|
{ .offset = 0x14, .name = "rx_red_prio_0", },
|
|
{ .offset = 0x15, .name = "rx_red_prio_1", },
|
|
{ .offset = 0x16, .name = "rx_red_prio_2", },
|
|
{ .offset = 0x17, .name = "rx_red_prio_3", },
|
|
{ .offset = 0x18, .name = "rx_red_prio_4", },
|
|
{ .offset = 0x19, .name = "rx_red_prio_5", },
|
|
{ .offset = 0x1A, .name = "rx_red_prio_6", },
|
|
{ .offset = 0x1B, .name = "rx_red_prio_7", },
|
|
{ .offset = 0x1C, .name = "rx_yellow_prio_0", },
|
|
{ .offset = 0x1D, .name = "rx_yellow_prio_1", },
|
|
{ .offset = 0x1E, .name = "rx_yellow_prio_2", },
|
|
{ .offset = 0x1F, .name = "rx_yellow_prio_3", },
|
|
{ .offset = 0x20, .name = "rx_yellow_prio_4", },
|
|
{ .offset = 0x21, .name = "rx_yellow_prio_5", },
|
|
{ .offset = 0x22, .name = "rx_yellow_prio_6", },
|
|
{ .offset = 0x23, .name = "rx_yellow_prio_7", },
|
|
{ .offset = 0x24, .name = "rx_green_prio_0", },
|
|
{ .offset = 0x25, .name = "rx_green_prio_1", },
|
|
{ .offset = 0x26, .name = "rx_green_prio_2", },
|
|
{ .offset = 0x27, .name = "rx_green_prio_3", },
|
|
{ .offset = 0x28, .name = "rx_green_prio_4", },
|
|
{ .offset = 0x29, .name = "rx_green_prio_5", },
|
|
{ .offset = 0x2A, .name = "rx_green_prio_6", },
|
|
{ .offset = 0x2B, .name = "rx_green_prio_7", },
|
|
{ .offset = 0x80, .name = "tx_octets", },
|
|
{ .offset = 0x81, .name = "tx_unicast", },
|
|
{ .offset = 0x82, .name = "tx_multicast", },
|
|
{ .offset = 0x83, .name = "tx_broadcast", },
|
|
{ .offset = 0x84, .name = "tx_collision", },
|
|
{ .offset = 0x85, .name = "tx_drops", },
|
|
{ .offset = 0x86, .name = "tx_pause", },
|
|
{ .offset = 0x87, .name = "tx_frames_below_65_octets", },
|
|
{ .offset = 0x88, .name = "tx_frames_65_to_127_octets", },
|
|
{ .offset = 0x89, .name = "tx_frames_128_255_octets", },
|
|
{ .offset = 0x8B, .name = "tx_frames_256_511_octets", },
|
|
{ .offset = 0x8C, .name = "tx_frames_1024_1526_octets", },
|
|
{ .offset = 0x8D, .name = "tx_frames_over_1526_octets", },
|
|
{ .offset = 0x8E, .name = "tx_yellow_prio_0", },
|
|
{ .offset = 0x8F, .name = "tx_yellow_prio_1", },
|
|
{ .offset = 0x90, .name = "tx_yellow_prio_2", },
|
|
{ .offset = 0x91, .name = "tx_yellow_prio_3", },
|
|
{ .offset = 0x92, .name = "tx_yellow_prio_4", },
|
|
{ .offset = 0x93, .name = "tx_yellow_prio_5", },
|
|
{ .offset = 0x94, .name = "tx_yellow_prio_6", },
|
|
{ .offset = 0x95, .name = "tx_yellow_prio_7", },
|
|
{ .offset = 0x96, .name = "tx_green_prio_0", },
|
|
{ .offset = 0x97, .name = "tx_green_prio_1", },
|
|
{ .offset = 0x98, .name = "tx_green_prio_2", },
|
|
{ .offset = 0x99, .name = "tx_green_prio_3", },
|
|
{ .offset = 0x9A, .name = "tx_green_prio_4", },
|
|
{ .offset = 0x9B, .name = "tx_green_prio_5", },
|
|
{ .offset = 0x9C, .name = "tx_green_prio_6", },
|
|
{ .offset = 0x9D, .name = "tx_green_prio_7", },
|
|
{ .offset = 0x9E, .name = "tx_aged", },
|
|
{ .offset = 0x100, .name = "drop_local", },
|
|
{ .offset = 0x101, .name = "drop_tail", },
|
|
{ .offset = 0x102, .name = "drop_yellow_prio_0", },
|
|
{ .offset = 0x103, .name = "drop_yellow_prio_1", },
|
|
{ .offset = 0x104, .name = "drop_yellow_prio_2", },
|
|
{ .offset = 0x105, .name = "drop_yellow_prio_3", },
|
|
{ .offset = 0x106, .name = "drop_yellow_prio_4", },
|
|
{ .offset = 0x107, .name = "drop_yellow_prio_5", },
|
|
{ .offset = 0x108, .name = "drop_yellow_prio_6", },
|
|
{ .offset = 0x109, .name = "drop_yellow_prio_7", },
|
|
{ .offset = 0x10A, .name = "drop_green_prio_0", },
|
|
{ .offset = 0x10B, .name = "drop_green_prio_1", },
|
|
{ .offset = 0x10C, .name = "drop_green_prio_2", },
|
|
{ .offset = 0x10D, .name = "drop_green_prio_3", },
|
|
{ .offset = 0x10E, .name = "drop_green_prio_4", },
|
|
{ .offset = 0x10F, .name = "drop_green_prio_5", },
|
|
{ .offset = 0x110, .name = "drop_green_prio_6", },
|
|
{ .offset = 0x111, .name = "drop_green_prio_7", },
|
|
};
|
|
|
|
static const struct vcap_field vsc9959_vcap_es0_keys[] = {
|
|
[VCAP_ES0_EGR_PORT] = { 0, 3},
|
|
[VCAP_ES0_IGR_PORT] = { 3, 3},
|
|
[VCAP_ES0_RSV] = { 6, 2},
|
|
[VCAP_ES0_L2_MC] = { 8, 1},
|
|
[VCAP_ES0_L2_BC] = { 9, 1},
|
|
[VCAP_ES0_VID] = { 10, 12},
|
|
[VCAP_ES0_DP] = { 22, 1},
|
|
[VCAP_ES0_PCP] = { 23, 3},
|
|
};
|
|
|
|
static const struct vcap_field vsc9959_vcap_es0_actions[] = {
|
|
[VCAP_ES0_ACT_PUSH_OUTER_TAG] = { 0, 2},
|
|
[VCAP_ES0_ACT_PUSH_INNER_TAG] = { 2, 1},
|
|
[VCAP_ES0_ACT_TAG_A_TPID_SEL] = { 3, 2},
|
|
[VCAP_ES0_ACT_TAG_A_VID_SEL] = { 5, 1},
|
|
[VCAP_ES0_ACT_TAG_A_PCP_SEL] = { 6, 2},
|
|
[VCAP_ES0_ACT_TAG_A_DEI_SEL] = { 8, 2},
|
|
[VCAP_ES0_ACT_TAG_B_TPID_SEL] = { 10, 2},
|
|
[VCAP_ES0_ACT_TAG_B_VID_SEL] = { 12, 1},
|
|
[VCAP_ES0_ACT_TAG_B_PCP_SEL] = { 13, 2},
|
|
[VCAP_ES0_ACT_TAG_B_DEI_SEL] = { 15, 2},
|
|
[VCAP_ES0_ACT_VID_A_VAL] = { 17, 12},
|
|
[VCAP_ES0_ACT_PCP_A_VAL] = { 29, 3},
|
|
[VCAP_ES0_ACT_DEI_A_VAL] = { 32, 1},
|
|
[VCAP_ES0_ACT_VID_B_VAL] = { 33, 12},
|
|
[VCAP_ES0_ACT_PCP_B_VAL] = { 45, 3},
|
|
[VCAP_ES0_ACT_DEI_B_VAL] = { 48, 1},
|
|
[VCAP_ES0_ACT_RSV] = { 49, 23},
|
|
[VCAP_ES0_ACT_HIT_STICKY] = { 72, 1},
|
|
};
|
|
|
|
static const struct vcap_field vsc9959_vcap_is1_keys[] = {
|
|
[VCAP_IS1_HK_TYPE] = { 0, 1},
|
|
[VCAP_IS1_HK_LOOKUP] = { 1, 2},
|
|
[VCAP_IS1_HK_IGR_PORT_MASK] = { 3, 7},
|
|
[VCAP_IS1_HK_RSV] = { 10, 9},
|
|
[VCAP_IS1_HK_OAM_Y1731] = { 19, 1},
|
|
[VCAP_IS1_HK_L2_MC] = { 20, 1},
|
|
[VCAP_IS1_HK_L2_BC] = { 21, 1},
|
|
[VCAP_IS1_HK_IP_MC] = { 22, 1},
|
|
[VCAP_IS1_HK_VLAN_TAGGED] = { 23, 1},
|
|
[VCAP_IS1_HK_VLAN_DBL_TAGGED] = { 24, 1},
|
|
[VCAP_IS1_HK_TPID] = { 25, 1},
|
|
[VCAP_IS1_HK_VID] = { 26, 12},
|
|
[VCAP_IS1_HK_DEI] = { 38, 1},
|
|
[VCAP_IS1_HK_PCP] = { 39, 3},
|
|
/* Specific Fields for IS1 Half Key S1_NORMAL */
|
|
[VCAP_IS1_HK_L2_SMAC] = { 42, 48},
|
|
[VCAP_IS1_HK_ETYPE_LEN] = { 90, 1},
|
|
[VCAP_IS1_HK_ETYPE] = { 91, 16},
|
|
[VCAP_IS1_HK_IP_SNAP] = {107, 1},
|
|
[VCAP_IS1_HK_IP4] = {108, 1},
|
|
/* Layer-3 Information */
|
|
[VCAP_IS1_HK_L3_FRAGMENT] = {109, 1},
|
|
[VCAP_IS1_HK_L3_FRAG_OFS_GT0] = {110, 1},
|
|
[VCAP_IS1_HK_L3_OPTIONS] = {111, 1},
|
|
[VCAP_IS1_HK_L3_DSCP] = {112, 6},
|
|
[VCAP_IS1_HK_L3_IP4_SIP] = {118, 32},
|
|
/* Layer-4 Information */
|
|
[VCAP_IS1_HK_TCP_UDP] = {150, 1},
|
|
[VCAP_IS1_HK_TCP] = {151, 1},
|
|
[VCAP_IS1_HK_L4_SPORT] = {152, 16},
|
|
[VCAP_IS1_HK_L4_RNG] = {168, 8},
|
|
/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */
|
|
[VCAP_IS1_HK_IP4_INNER_TPID] = { 42, 1},
|
|
[VCAP_IS1_HK_IP4_INNER_VID] = { 43, 12},
|
|
[VCAP_IS1_HK_IP4_INNER_DEI] = { 55, 1},
|
|
[VCAP_IS1_HK_IP4_INNER_PCP] = { 56, 3},
|
|
[VCAP_IS1_HK_IP4_IP4] = { 59, 1},
|
|
[VCAP_IS1_HK_IP4_L3_FRAGMENT] = { 60, 1},
|
|
[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0] = { 61, 1},
|
|
[VCAP_IS1_HK_IP4_L3_OPTIONS] = { 62, 1},
|
|
[VCAP_IS1_HK_IP4_L3_DSCP] = { 63, 6},
|
|
[VCAP_IS1_HK_IP4_L3_IP4_DIP] = { 69, 32},
|
|
[VCAP_IS1_HK_IP4_L3_IP4_SIP] = {101, 32},
|
|
[VCAP_IS1_HK_IP4_L3_PROTO] = {133, 8},
|
|
[VCAP_IS1_HK_IP4_TCP_UDP] = {141, 1},
|
|
[VCAP_IS1_HK_IP4_TCP] = {142, 1},
|
|
[VCAP_IS1_HK_IP4_L4_RNG] = {143, 8},
|
|
[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE] = {151, 32},
|
|
};
|
|
|
|
static const struct vcap_field vsc9959_vcap_is1_actions[] = {
|
|
[VCAP_IS1_ACT_DSCP_ENA] = { 0, 1},
|
|
[VCAP_IS1_ACT_DSCP_VAL] = { 1, 6},
|
|
[VCAP_IS1_ACT_QOS_ENA] = { 7, 1},
|
|
[VCAP_IS1_ACT_QOS_VAL] = { 8, 3},
|
|
[VCAP_IS1_ACT_DP_ENA] = { 11, 1},
|
|
[VCAP_IS1_ACT_DP_VAL] = { 12, 1},
|
|
[VCAP_IS1_ACT_PAG_OVERRIDE_MASK] = { 13, 8},
|
|
[VCAP_IS1_ACT_PAG_VAL] = { 21, 8},
|
|
[VCAP_IS1_ACT_RSV] = { 29, 9},
|
|
/* The fields below are incorrectly shifted by 2 in the manual */
|
|
[VCAP_IS1_ACT_VID_REPLACE_ENA] = { 38, 1},
|
|
[VCAP_IS1_ACT_VID_ADD_VAL] = { 39, 12},
|
|
[VCAP_IS1_ACT_FID_SEL] = { 51, 2},
|
|
[VCAP_IS1_ACT_FID_VAL] = { 53, 13},
|
|
[VCAP_IS1_ACT_PCP_DEI_ENA] = { 66, 1},
|
|
[VCAP_IS1_ACT_PCP_VAL] = { 67, 3},
|
|
[VCAP_IS1_ACT_DEI_VAL] = { 70, 1},
|
|
[VCAP_IS1_ACT_VLAN_POP_CNT_ENA] = { 71, 1},
|
|
[VCAP_IS1_ACT_VLAN_POP_CNT] = { 72, 2},
|
|
[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA] = { 74, 4},
|
|
[VCAP_IS1_ACT_HIT_STICKY] = { 78, 1},
|
|
};
|
|
|
|
static struct vcap_field vsc9959_vcap_is2_keys[] = {
|
|
/* Common: 41 bits */
|
|
[VCAP_IS2_TYPE] = { 0, 4},
|
|
[VCAP_IS2_HK_FIRST] = { 4, 1},
|
|
[VCAP_IS2_HK_PAG] = { 5, 8},
|
|
[VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 7},
|
|
[VCAP_IS2_HK_RSV2] = { 20, 1},
|
|
[VCAP_IS2_HK_HOST_MATCH] = { 21, 1},
|
|
[VCAP_IS2_HK_L2_MC] = { 22, 1},
|
|
[VCAP_IS2_HK_L2_BC] = { 23, 1},
|
|
[VCAP_IS2_HK_VLAN_TAGGED] = { 24, 1},
|
|
[VCAP_IS2_HK_VID] = { 25, 12},
|
|
[VCAP_IS2_HK_DEI] = { 37, 1},
|
|
[VCAP_IS2_HK_PCP] = { 38, 3},
|
|
/* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */
|
|
[VCAP_IS2_HK_L2_DMAC] = { 41, 48},
|
|
[VCAP_IS2_HK_L2_SMAC] = { 89, 48},
|
|
/* MAC_ETYPE (TYPE=000) */
|
|
[VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {137, 16},
|
|
[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {153, 16},
|
|
[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {169, 8},
|
|
[VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {177, 3},
|
|
/* MAC_LLC (TYPE=001) */
|
|
[VCAP_IS2_HK_MAC_LLC_L2_LLC] = {137, 40},
|
|
/* MAC_SNAP (TYPE=010) */
|
|
[VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {137, 40},
|
|
/* MAC_ARP (TYPE=011) */
|
|
[VCAP_IS2_HK_MAC_ARP_SMAC] = { 41, 48},
|
|
[VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 89, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 90, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 91, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 92, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 93, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 94, 1},
|
|
[VCAP_IS2_HK_MAC_ARP_OPCODE] = { 95, 2},
|
|
[VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = { 97, 32},
|
|
[VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {129, 32},
|
|
[VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {161, 1},
|
|
/* IP4_TCP_UDP / IP4_OTHER common */
|
|
[VCAP_IS2_HK_IP4] = { 41, 1},
|
|
[VCAP_IS2_HK_L3_FRAGMENT] = { 42, 1},
|
|
[VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 43, 1},
|
|
[VCAP_IS2_HK_L3_OPTIONS] = { 44, 1},
|
|
[VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 45, 1},
|
|
[VCAP_IS2_HK_L3_TOS] = { 46, 8},
|
|
[VCAP_IS2_HK_L3_IP4_DIP] = { 54, 32},
|
|
[VCAP_IS2_HK_L3_IP4_SIP] = { 86, 32},
|
|
[VCAP_IS2_HK_DIP_EQ_SIP] = {118, 1},
|
|
/* IP4_TCP_UDP (TYPE=100) */
|
|
[VCAP_IS2_HK_TCP] = {119, 1},
|
|
[VCAP_IS2_HK_L4_DPORT] = {120, 16},
|
|
[VCAP_IS2_HK_L4_SPORT] = {136, 16},
|
|
[VCAP_IS2_HK_L4_RNG] = {152, 8},
|
|
[VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {160, 1},
|
|
[VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {161, 1},
|
|
[VCAP_IS2_HK_L4_FIN] = {162, 1},
|
|
[VCAP_IS2_HK_L4_SYN] = {163, 1},
|
|
[VCAP_IS2_HK_L4_RST] = {164, 1},
|
|
[VCAP_IS2_HK_L4_PSH] = {165, 1},
|
|
[VCAP_IS2_HK_L4_ACK] = {166, 1},
|
|
[VCAP_IS2_HK_L4_URG] = {167, 1},
|
|
[VCAP_IS2_HK_L4_1588_DOM] = {168, 8},
|
|
[VCAP_IS2_HK_L4_1588_VER] = {176, 4},
|
|
/* IP4_OTHER (TYPE=101) */
|
|
[VCAP_IS2_HK_IP4_L3_PROTO] = {119, 8},
|
|
[VCAP_IS2_HK_L3_PAYLOAD] = {127, 56},
|
|
/* IP6_STD (TYPE=110) */
|
|
[VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 41, 1},
|
|
[VCAP_IS2_HK_L3_IP6_SIP] = { 42, 128},
|
|
[VCAP_IS2_HK_IP6_L3_PROTO] = {170, 8},
|
|
/* OAM (TYPE=111) */
|
|
[VCAP_IS2_HK_OAM_MEL_FLAGS] = {137, 7},
|
|
[VCAP_IS2_HK_OAM_VER] = {144, 5},
|
|
[VCAP_IS2_HK_OAM_OPCODE] = {149, 8},
|
|
[VCAP_IS2_HK_OAM_FLAGS] = {157, 8},
|
|
[VCAP_IS2_HK_OAM_MEPID] = {165, 16},
|
|
[VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {181, 1},
|
|
[VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1},
|
|
};
|
|
|
|
static struct vcap_field vsc9959_vcap_is2_actions[] = {
|
|
[VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1},
|
|
[VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1},
|
|
[VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3},
|
|
[VCAP_IS2_ACT_MASK_MODE] = { 5, 2},
|
|
[VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1},
|
|
[VCAP_IS2_ACT_LRN_DIS] = { 8, 1},
|
|
[VCAP_IS2_ACT_POLICE_ENA] = { 9, 1},
|
|
[VCAP_IS2_ACT_POLICE_IDX] = { 10, 9},
|
|
[VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1},
|
|
[VCAP_IS2_ACT_PORT_MASK] = { 20, 6},
|
|
[VCAP_IS2_ACT_REW_OP] = { 26, 9},
|
|
[VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 35, 1},
|
|
[VCAP_IS2_ACT_RSV] = { 36, 2},
|
|
[VCAP_IS2_ACT_ACL_ID] = { 38, 6},
|
|
[VCAP_IS2_ACT_HIT_CNT] = { 44, 32},
|
|
};
|
|
|
|
static struct vcap_props vsc9959_vcap_props[] = {
|
|
[VCAP_ES0] = {
|
|
.action_type_width = 0,
|
|
.action_table = {
|
|
[ES0_ACTION_TYPE_NORMAL] = {
|
|
.width = 72, /* HIT_STICKY not included */
|
|
.count = 1,
|
|
},
|
|
},
|
|
.target = S0,
|
|
.keys = vsc9959_vcap_es0_keys,
|
|
.actions = vsc9959_vcap_es0_actions,
|
|
},
|
|
[VCAP_IS1] = {
|
|
.action_type_width = 0,
|
|
.action_table = {
|
|
[IS1_ACTION_TYPE_NORMAL] = {
|
|
.width = 78, /* HIT_STICKY not included */
|
|
.count = 4,
|
|
},
|
|
},
|
|
.target = S1,
|
|
.keys = vsc9959_vcap_is1_keys,
|
|
.actions = vsc9959_vcap_is1_actions,
|
|
},
|
|
[VCAP_IS2] = {
|
|
.action_type_width = 1,
|
|
.action_table = {
|
|
[IS2_ACTION_TYPE_NORMAL] = {
|
|
.width = 44,
|
|
.count = 2
|
|
},
|
|
[IS2_ACTION_TYPE_SMAC_SIP] = {
|
|
.width = 6,
|
|
.count = 4
|
|
},
|
|
},
|
|
.target = S2,
|
|
.keys = vsc9959_vcap_is2_keys,
|
|
.actions = vsc9959_vcap_is2_actions,
|
|
},
|
|
};
|
|
|
|
static const struct ptp_clock_info vsc9959_ptp_caps = {
|
|
.owner = THIS_MODULE,
|
|
.name = "felix ptp",
|
|
.max_adj = 0x7fffffff,
|
|
.n_alarm = 0,
|
|
.n_ext_ts = 0,
|
|
.n_per_out = OCELOT_PTP_PINS_NUM,
|
|
.n_pins = OCELOT_PTP_PINS_NUM,
|
|
.pps = 0,
|
|
.gettime64 = ocelot_ptp_gettime64,
|
|
.settime64 = ocelot_ptp_settime64,
|
|
.adjtime = ocelot_ptp_adjtime,
|
|
.adjfine = ocelot_ptp_adjfine,
|
|
.verify = ocelot_ptp_verify,
|
|
.enable = ocelot_ptp_enable,
|
|
};
|
|
|
|
#define VSC9959_INIT_TIMEOUT 50000
|
|
#define VSC9959_GCB_RST_SLEEP 100
|
|
#define VSC9959_SYS_RAMINIT_SLEEP 80
|
|
|
|
static int vsc9959_gcb_soft_rst_status(struct ocelot *ocelot)
|
|
{
|
|
int val;
|
|
|
|
ocelot_field_read(ocelot, GCB_SOFT_RST_SWC_RST, &val);
|
|
|
|
return val;
|
|
}
|
|
|
|
static int vsc9959_sys_ram_init_status(struct ocelot *ocelot)
|
|
{
|
|
return ocelot_read(ocelot, SYS_RAM_INIT);
|
|
}
|
|
|
|
/* CORE_ENA is in SYS:SYSTEM:RESET_CFG
|
|
* RAM_INIT is in SYS:RAM_CTRL:RAM_INIT
|
|
*/
|
|
static int vsc9959_reset(struct ocelot *ocelot)
|
|
{
|
|
int val, err;
|
|
|
|
/* soft-reset the switch core */
|
|
ocelot_field_write(ocelot, GCB_SOFT_RST_SWC_RST, 1);
|
|
|
|
err = readx_poll_timeout(vsc9959_gcb_soft_rst_status, ocelot, val, !val,
|
|
VSC9959_GCB_RST_SLEEP, VSC9959_INIT_TIMEOUT);
|
|
if (err) {
|
|
dev_err(ocelot->dev, "timeout: switch core reset\n");
|
|
return err;
|
|
}
|
|
|
|
/* initialize switch mem ~40us */
|
|
ocelot_write(ocelot, SYS_RAM_INIT_RAM_INIT, SYS_RAM_INIT);
|
|
err = readx_poll_timeout(vsc9959_sys_ram_init_status, ocelot, val, !val,
|
|
VSC9959_SYS_RAMINIT_SLEEP,
|
|
VSC9959_INIT_TIMEOUT);
|
|
if (err) {
|
|
dev_err(ocelot->dev, "timeout: switch sram init\n");
|
|
return err;
|
|
}
|
|
|
|
/* enable switch core */
|
|
ocelot_field_write(ocelot, SYS_RESET_CFG_CORE_ENA, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vsc9959_phylink_validate(struct ocelot *ocelot, int port,
|
|
unsigned long *supported,
|
|
struct phylink_link_state *state)
|
|
{
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
|
|
if (state->interface != PHY_INTERFACE_MODE_NA &&
|
|
state->interface != ocelot_port->phy_mode) {
|
|
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
return;
|
|
}
|
|
|
|
phylink_set_port_modes(mask);
|
|
phylink_set(mask, Autoneg);
|
|
phylink_set(mask, Pause);
|
|
phylink_set(mask, Asym_Pause);
|
|
phylink_set(mask, 10baseT_Half);
|
|
phylink_set(mask, 10baseT_Full);
|
|
phylink_set(mask, 100baseT_Half);
|
|
phylink_set(mask, 100baseT_Full);
|
|
phylink_set(mask, 1000baseT_Half);
|
|
phylink_set(mask, 1000baseT_Full);
|
|
|
|
if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
|
|
state->interface == PHY_INTERFACE_MODE_2500BASEX ||
|
|
state->interface == PHY_INTERFACE_MODE_USXGMII) {
|
|
phylink_set(mask, 2500baseT_Full);
|
|
phylink_set(mask, 2500baseX_Full);
|
|
}
|
|
|
|
bitmap_and(supported, supported, mask,
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
bitmap_and(state->advertising, state->advertising, mask,
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
}
|
|
|
|
static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port,
|
|
phy_interface_t phy_mode)
|
|
{
|
|
switch (phy_mode) {
|
|
case PHY_INTERFACE_MODE_INTERNAL:
|
|
if (port != 4 && port != 5)
|
|
return -ENOTSUPP;
|
|
return 0;
|
|
case PHY_INTERFACE_MODE_SGMII:
|
|
case PHY_INTERFACE_MODE_QSGMII:
|
|
case PHY_INTERFACE_MODE_USXGMII:
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
|
/* Not supported on internal to-CPU ports */
|
|
if (port == 4 || port == 5)
|
|
return -ENOTSUPP;
|
|
return 0;
|
|
default:
|
|
return -ENOTSUPP;
|
|
}
|
|
}
|
|
|
|
/* Watermark encode
|
|
* Bit 8: Unit; 0:1, 1:16
|
|
* Bit 7-0: Value to be multiplied with unit
|
|
*/
|
|
static u16 vsc9959_wm_enc(u16 value)
|
|
{
|
|
WARN_ON(value >= 16 * BIT(8));
|
|
|
|
if (value >= BIT(8))
|
|
return BIT(8) | (value / 16);
|
|
|
|
return value;
|
|
}
|
|
|
|
static const struct ocelot_ops vsc9959_ops = {
|
|
.reset = vsc9959_reset,
|
|
.wm_enc = vsc9959_wm_enc,
|
|
.port_to_netdev = felix_port_to_netdev,
|
|
.netdev_to_port = felix_netdev_to_port,
|
|
};
|
|
|
|
static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
|
|
{
|
|
struct felix *felix = ocelot_to_felix(ocelot);
|
|
struct enetc_mdio_priv *mdio_priv;
|
|
struct device *dev = ocelot->dev;
|
|
void __iomem *imdio_regs;
|
|
struct resource res;
|
|
struct enetc_hw *hw;
|
|
struct mii_bus *bus;
|
|
int port;
|
|
int rc;
|
|
|
|
felix->pcs = devm_kcalloc(dev, felix->info->num_ports,
|
|
sizeof(struct lynx_pcs *),
|
|
GFP_KERNEL);
|
|
if (!felix->pcs) {
|
|
dev_err(dev, "failed to allocate array for PCS PHYs\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(&res, felix->info->imdio_res, sizeof(res));
|
|
res.flags = IORESOURCE_MEM;
|
|
res.start += felix->imdio_base;
|
|
res.end += felix->imdio_base;
|
|
|
|
imdio_regs = devm_ioremap_resource(dev, &res);
|
|
if (IS_ERR(imdio_regs)) {
|
|
dev_err(dev, "failed to map internal MDIO registers\n");
|
|
return PTR_ERR(imdio_regs);
|
|
}
|
|
|
|
hw = enetc_hw_alloc(dev, imdio_regs);
|
|
if (IS_ERR(hw)) {
|
|
dev_err(dev, "failed to allocate ENETC HW structure\n");
|
|
return PTR_ERR(hw);
|
|
}
|
|
|
|
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
|
|
if (!bus)
|
|
return -ENOMEM;
|
|
|
|
bus->name = "VSC9959 internal MDIO bus";
|
|
bus->read = enetc_mdio_read;
|
|
bus->write = enetc_mdio_write;
|
|
bus->parent = dev;
|
|
mdio_priv = bus->priv;
|
|
mdio_priv->hw = hw;
|
|
/* This gets added to imdio_regs, which already maps addresses
|
|
* starting with the proper offset.
|
|
*/
|
|
mdio_priv->mdio_base = 0;
|
|
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
|
|
|
|
/* Needed in order to initialize the bus mutex lock */
|
|
rc = mdiobus_register(bus);
|
|
if (rc < 0) {
|
|
dev_err(dev, "failed to register MDIO bus\n");
|
|
return rc;
|
|
}
|
|
|
|
felix->imdio = bus;
|
|
|
|
for (port = 0; port < felix->info->num_ports; port++) {
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
struct mdio_device *pcs;
|
|
struct lynx_pcs *lynx;
|
|
|
|
if (dsa_is_unused_port(felix->ds, port))
|
|
continue;
|
|
|
|
if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
|
|
continue;
|
|
|
|
pcs = mdio_device_create(felix->imdio, port);
|
|
if (IS_ERR(pcs))
|
|
continue;
|
|
|
|
lynx = lynx_pcs_create(pcs);
|
|
if (!lynx) {
|
|
mdio_device_free(pcs);
|
|
continue;
|
|
}
|
|
|
|
felix->pcs[port] = lynx;
|
|
|
|
dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
|
|
{
|
|
struct felix *felix = ocelot_to_felix(ocelot);
|
|
int port;
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
struct lynx_pcs *pcs = felix->pcs[port];
|
|
|
|
if (!pcs)
|
|
continue;
|
|
|
|
mdio_device_free(pcs->mdio);
|
|
lynx_pcs_destroy(pcs);
|
|
}
|
|
mdiobus_unregister(felix->imdio);
|
|
}
|
|
|
|
static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
|
|
u32 speed)
|
|
{
|
|
u8 tas_speed;
|
|
|
|
switch (speed) {
|
|
case SPEED_10:
|
|
tas_speed = OCELOT_SPEED_10;
|
|
break;
|
|
case SPEED_100:
|
|
tas_speed = OCELOT_SPEED_100;
|
|
break;
|
|
case SPEED_1000:
|
|
tas_speed = OCELOT_SPEED_1000;
|
|
break;
|
|
case SPEED_2500:
|
|
tas_speed = OCELOT_SPEED_2500;
|
|
break;
|
|
default:
|
|
tas_speed = OCELOT_SPEED_1000;
|
|
break;
|
|
}
|
|
|
|
ocelot_rmw_rix(ocelot,
|
|
QSYS_TAG_CONFIG_LINK_SPEED(tas_speed),
|
|
QSYS_TAG_CONFIG_LINK_SPEED_M,
|
|
QSYS_TAG_CONFIG, port);
|
|
}
|
|
|
|
static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
|
|
u64 cycle_time,
|
|
struct timespec64 *new_base_ts)
|
|
{
|
|
struct timespec64 ts;
|
|
ktime_t new_base_time;
|
|
ktime_t current_time;
|
|
|
|
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
|
|
current_time = timespec64_to_ktime(ts);
|
|
new_base_time = base_time;
|
|
|
|
if (base_time < current_time) {
|
|
u64 nr_of_cycles = current_time - base_time;
|
|
|
|
do_div(nr_of_cycles, cycle_time);
|
|
new_base_time += cycle_time * (nr_of_cycles + 1);
|
|
}
|
|
|
|
*new_base_ts = ktime_to_timespec64(new_base_time);
|
|
}
|
|
|
|
static u32 vsc9959_tas_read_cfg_status(struct ocelot *ocelot)
|
|
{
|
|
return ocelot_read(ocelot, QSYS_TAS_PARAM_CFG_CTRL);
|
|
}
|
|
|
|
static void vsc9959_tas_gcl_set(struct ocelot *ocelot, const u32 gcl_ix,
|
|
struct tc_taprio_sched_entry *entry)
|
|
{
|
|
ocelot_write(ocelot,
|
|
QSYS_GCL_CFG_REG_1_GCL_ENTRY_NUM(gcl_ix) |
|
|
QSYS_GCL_CFG_REG_1_GATE_STATE(entry->gate_mask),
|
|
QSYS_GCL_CFG_REG_1);
|
|
ocelot_write(ocelot, entry->interval, QSYS_GCL_CFG_REG_2);
|
|
}
|
|
|
|
static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
|
|
struct tc_taprio_qopt_offload *taprio)
|
|
{
|
|
struct timespec64 base_ts;
|
|
int ret, i;
|
|
u32 val;
|
|
|
|
if (!taprio->enable) {
|
|
ocelot_rmw_rix(ocelot,
|
|
QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF),
|
|
QSYS_TAG_CONFIG_ENABLE |
|
|
QSYS_TAG_CONFIG_INIT_GATE_STATE_M,
|
|
QSYS_TAG_CONFIG, port);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (taprio->cycle_time > NSEC_PER_SEC ||
|
|
taprio->cycle_time_extension >= NSEC_PER_SEC)
|
|
return -EINVAL;
|
|
|
|
if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX)
|
|
return -ERANGE;
|
|
|
|
ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(port) |
|
|
QSYS_TAS_PARAM_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q,
|
|
QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M |
|
|
QSYS_TAS_PARAM_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q,
|
|
QSYS_TAS_PARAM_CFG_CTRL);
|
|
|
|
/* Hardware errata - Admin config could not be overwritten if
|
|
* config is pending, need reset the TAS module
|
|
*/
|
|
val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8);
|
|
if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING)
|
|
return -EBUSY;
|
|
|
|
ocelot_rmw_rix(ocelot,
|
|
QSYS_TAG_CONFIG_ENABLE |
|
|
QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF) |
|
|
QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES(0xFF),
|
|
QSYS_TAG_CONFIG_ENABLE |
|
|
QSYS_TAG_CONFIG_INIT_GATE_STATE_M |
|
|
QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_M,
|
|
QSYS_TAG_CONFIG, port);
|
|
|
|
vsc9959_new_base_time(ocelot, taprio->base_time,
|
|
taprio->cycle_time, &base_ts);
|
|
ocelot_write(ocelot, base_ts.tv_nsec, QSYS_PARAM_CFG_REG_1);
|
|
ocelot_write(ocelot, lower_32_bits(base_ts.tv_sec), QSYS_PARAM_CFG_REG_2);
|
|
val = upper_32_bits(base_ts.tv_sec);
|
|
ocelot_write(ocelot,
|
|
QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB(val) |
|
|
QSYS_PARAM_CFG_REG_3_LIST_LENGTH(taprio->num_entries),
|
|
QSYS_PARAM_CFG_REG_3);
|
|
ocelot_write(ocelot, taprio->cycle_time, QSYS_PARAM_CFG_REG_4);
|
|
ocelot_write(ocelot, taprio->cycle_time_extension, QSYS_PARAM_CFG_REG_5);
|
|
|
|
for (i = 0; i < taprio->num_entries; i++)
|
|
vsc9959_tas_gcl_set(ocelot, i, &taprio->entries[i]);
|
|
|
|
ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
|
|
QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
|
|
QSYS_TAS_PARAM_CFG_CTRL);
|
|
|
|
ret = readx_poll_timeout(vsc9959_tas_read_cfg_status, ocelot, val,
|
|
!(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE),
|
|
10, 100000);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port,
|
|
struct tc_cbs_qopt_offload *cbs_qopt)
|
|
{
|
|
struct ocelot *ocelot = ds->priv;
|
|
int port_ix = port * 8 + cbs_qopt->queue;
|
|
u32 rate, burst;
|
|
|
|
if (cbs_qopt->queue >= ds->num_tx_queues)
|
|
return -EINVAL;
|
|
|
|
if (!cbs_qopt->enable) {
|
|
ocelot_write_gix(ocelot, QSYS_CIR_CFG_CIR_RATE(0) |
|
|
QSYS_CIR_CFG_CIR_BURST(0),
|
|
QSYS_CIR_CFG, port_ix);
|
|
|
|
ocelot_rmw_gix(ocelot, 0, QSYS_SE_CFG_SE_AVB_ENA,
|
|
QSYS_SE_CFG, port_ix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Rate unit is 100 kbps */
|
|
rate = DIV_ROUND_UP(cbs_qopt->idleslope, 100);
|
|
/* Avoid using zero rate */
|
|
rate = clamp_t(u32, rate, 1, GENMASK(14, 0));
|
|
/* Burst unit is 4kB */
|
|
burst = DIV_ROUND_UP(cbs_qopt->hicredit, 4096);
|
|
/* Avoid using zero burst size */
|
|
burst = clamp_t(u32, burst, 1, GENMASK(5, 0));
|
|
ocelot_write_gix(ocelot,
|
|
QSYS_CIR_CFG_CIR_RATE(rate) |
|
|
QSYS_CIR_CFG_CIR_BURST(burst),
|
|
QSYS_CIR_CFG,
|
|
port_ix);
|
|
|
|
ocelot_rmw_gix(ocelot,
|
|
QSYS_SE_CFG_SE_FRM_MODE(0) |
|
|
QSYS_SE_CFG_SE_AVB_ENA,
|
|
QSYS_SE_CFG_SE_AVB_ENA |
|
|
QSYS_SE_CFG_SE_FRM_MODE_M,
|
|
QSYS_SE_CFG,
|
|
port_ix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,
|
|
enum tc_setup_type type,
|
|
void *type_data)
|
|
{
|
|
struct ocelot *ocelot = ds->priv;
|
|
|
|
switch (type) {
|
|
case TC_SETUP_QDISC_TAPRIO:
|
|
return vsc9959_qos_port_tas_set(ocelot, port, type_data);
|
|
case TC_SETUP_QDISC_CBS:
|
|
return vsc9959_qos_port_cbs_set(ds, port, type_data);
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
static void vsc9959_xmit_template_populate(struct ocelot *ocelot, int port)
|
|
{
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
u8 *template = ocelot_port->xmit_template;
|
|
u64 bypass, dest, src;
|
|
__be32 *prefix;
|
|
u8 *injection;
|
|
|
|
/* Set the source port as the CPU port module and not the
|
|
* NPI port
|
|
*/
|
|
src = ocelot->num_phys_ports;
|
|
dest = BIT(port);
|
|
bypass = true;
|
|
|
|
injection = template + OCELOT_SHORT_PREFIX_LEN;
|
|
prefix = (__be32 *)template;
|
|
|
|
packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0);
|
|
packing(injection, &dest, 68, 56, OCELOT_TAG_LEN, PACK, 0);
|
|
packing(injection, &src, 46, 43, OCELOT_TAG_LEN, PACK, 0);
|
|
|
|
*prefix = cpu_to_be32(0x8880000a);
|
|
}
|
|
|
|
static const struct felix_info felix_info_vsc9959 = {
|
|
.target_io_res = vsc9959_target_io_res,
|
|
.port_io_res = vsc9959_port_io_res,
|
|
.imdio_res = &vsc9959_imdio_res,
|
|
.regfields = vsc9959_regfields,
|
|
.map = vsc9959_regmap,
|
|
.ops = &vsc9959_ops,
|
|
.stats_layout = vsc9959_stats_layout,
|
|
.num_stats = ARRAY_SIZE(vsc9959_stats_layout),
|
|
.vcap = vsc9959_vcap_props,
|
|
.shared_queue_sz = 128 * 1024,
|
|
.num_mact_rows = 2048,
|
|
.num_ports = 6,
|
|
.num_tx_queues = FELIX_NUM_TC,
|
|
.switch_pci_bar = 4,
|
|
.imdio_pci_bar = 0,
|
|
.ptp_caps = &vsc9959_ptp_caps,
|
|
.mdio_bus_alloc = vsc9959_mdio_bus_alloc,
|
|
.mdio_bus_free = vsc9959_mdio_bus_free,
|
|
.phylink_validate = vsc9959_phylink_validate,
|
|
.prevalidate_phy_mode = vsc9959_prevalidate_phy_mode,
|
|
.port_setup_tc = vsc9959_port_setup_tc,
|
|
.port_sched_speed_set = vsc9959_sched_speed_set,
|
|
.xmit_template_populate = vsc9959_xmit_template_populate,
|
|
};
|
|
|
|
static irqreturn_t felix_irq_handler(int irq, void *data)
|
|
{
|
|
struct ocelot *ocelot = (struct ocelot *)data;
|
|
|
|
/* The INTB interrupt is used for both PTP TX timestamp interrupt
|
|
* and preemption status change interrupt on each port.
|
|
*
|
|
* - Get txtstamp if have
|
|
* - TODO: handle preemption. Without handling it, driver may get
|
|
* interrupt storm.
|
|
*/
|
|
|
|
ocelot_get_txtstamp(ocelot);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int felix_pci_probe(struct pci_dev *pdev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
struct dsa_switch *ds;
|
|
struct ocelot *ocelot;
|
|
struct felix *felix;
|
|
int err;
|
|
|
|
if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
|
|
dev_info(&pdev->dev, "device is disabled, skipping\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
err = pci_enable_device(pdev);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "device enable failed\n");
|
|
goto err_pci_enable;
|
|
}
|
|
|
|
/* set up for high or low dma */
|
|
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
|
if (err) {
|
|
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
if (err) {
|
|
dev_err(&pdev->dev,
|
|
"DMA configuration failed: 0x%x\n", err);
|
|
goto err_dma;
|
|
}
|
|
}
|
|
|
|
felix = kzalloc(sizeof(struct felix), GFP_KERNEL);
|
|
if (!felix) {
|
|
err = -ENOMEM;
|
|
dev_err(&pdev->dev, "Failed to allocate driver memory\n");
|
|
goto err_alloc_felix;
|
|
}
|
|
|
|
pci_set_drvdata(pdev, felix);
|
|
ocelot = &felix->ocelot;
|
|
ocelot->dev = &pdev->dev;
|
|
ocelot->num_flooding_pgids = FELIX_NUM_TC;
|
|
felix->info = &felix_info_vsc9959;
|
|
felix->switch_base = pci_resource_start(pdev,
|
|
felix->info->switch_pci_bar);
|
|
felix->imdio_base = pci_resource_start(pdev,
|
|
felix->info->imdio_pci_bar);
|
|
|
|
pci_set_master(pdev);
|
|
|
|
err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL,
|
|
&felix_irq_handler, IRQF_ONESHOT,
|
|
"felix-intb", ocelot);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Failed to request irq\n");
|
|
goto err_alloc_irq;
|
|
}
|
|
|
|
ocelot->ptp = 1;
|
|
|
|
ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL);
|
|
if (!ds) {
|
|
err = -ENOMEM;
|
|
dev_err(&pdev->dev, "Failed to allocate DSA switch\n");
|
|
goto err_alloc_ds;
|
|
}
|
|
|
|
ds->dev = &pdev->dev;
|
|
ds->num_ports = felix->info->num_ports;
|
|
ds->num_tx_queues = felix->info->num_tx_queues;
|
|
ds->ops = &felix_switch_ops;
|
|
ds->priv = ocelot;
|
|
felix->ds = ds;
|
|
|
|
err = dsa_register_switch(ds);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err);
|
|
goto err_register_ds;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_register_ds:
|
|
kfree(ds);
|
|
err_alloc_ds:
|
|
err_alloc_irq:
|
|
err_alloc_felix:
|
|
kfree(felix);
|
|
err_dma:
|
|
pci_disable_device(pdev);
|
|
err_pci_enable:
|
|
return err;
|
|
}
|
|
|
|
static void felix_pci_remove(struct pci_dev *pdev)
|
|
{
|
|
struct felix *felix;
|
|
|
|
felix = pci_get_drvdata(pdev);
|
|
|
|
dsa_unregister_switch(felix->ds);
|
|
|
|
kfree(felix->ds);
|
|
kfree(felix);
|
|
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
static struct pci_device_id felix_ids[] = {
|
|
{
|
|
/* NXP LS1028A */
|
|
PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0xEEF0),
|
|
},
|
|
{ 0, }
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, felix_ids);
|
|
|
|
static struct pci_driver felix_vsc9959_pci_driver = {
|
|
.name = "mscc_felix",
|
|
.id_table = felix_ids,
|
|
.probe = felix_pci_probe,
|
|
.remove = felix_pci_remove,
|
|
};
|
|
module_pci_driver(felix_vsc9959_pci_driver);
|
|
|
|
MODULE_DESCRIPTION("Felix Switch driver");
|
|
MODULE_LICENSE("GPL v2");
|