1*336e3e4aSAlvin Šipraga // SPDX-License-Identifier: GPL-2.0 2*336e3e4aSAlvin Šipraga /* Forwarding and multicast database interface for the rtl8365mb switch family 3*336e3e4aSAlvin Šipraga * 4*336e3e4aSAlvin Šipraga * Copyright (C) 2022 Alvin Šipraga <alsi@bang-olufsen.dk> 5*336e3e4aSAlvin Šipraga */ 6*336e3e4aSAlvin Šipraga 7*336e3e4aSAlvin Šipraga #include <linux/etherdevice.h> 8*336e3e4aSAlvin Šipraga 9*336e3e4aSAlvin Šipraga #include "rtl8365mb_l2.h" 10*336e3e4aSAlvin Šipraga #include "rtl8365mb_table.h" 11*336e3e4aSAlvin Šipraga #include <linux/regmap.h> 12*336e3e4aSAlvin Šipraga 13*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_ENTRY_SIZE 6 14*336e3e4aSAlvin Šipraga 15*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D0_MAC5_MSK GENMASK(7, 0) 16*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D0_MAC4_MSK GENMASK(15, 8) 17*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D1_MAC3_MSK GENMASK(7, 0) 18*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D1_MAC2_MSK GENMASK(15, 8) 19*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D2_MAC1_MSK GENMASK(7, 0) 20*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D2_MAC0_MSK GENMASK(15, 8) 21*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_VID_MSK GENMASK(11, 0) 22*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_IVL_MSK GENMASK(13, 13) 23*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_PORT_EXT_MSK GENMASK(15, 15) 24*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_PORT_HI_MSK GENMASK(3, 3) 25*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_EFID_MSK GENMASK(2, 0) 26*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_FID_MSK GENMASK(6, 3) 27*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_SA_PRI_MSK GENMASK(7, 7) 28*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_PORT_MSK GENMASK(10, 8) 29*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_PORT_LO_MSK GENMASK(2, 0) 30*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_AGE_MSK GENMASK(13, 11) 31*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_AUTH_MSK GENMASK(14, 14) 32*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_SA_BLOCK_MSK GENMASK(15, 15) 33*336e3e4aSAlvin Šipraga 34*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_DA_BLOCK_MSK GENMASK(0, 0) 35*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_PRIORITY_MSK GENMASK(3, 1) 36*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_FWD_PRI_MSK GENMASK(4, 4) 37*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_STATIC_MSK GENMASK(5, 5) 38*336e3e4aSAlvin Šipraga 39*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D0_MAC5_MSK GENMASK(7, 0) 40*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D0_MAC4_MSK GENMASK(15, 8) 41*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D1_MAC3_MSK GENMASK(7, 0) 42*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D1_MAC2_MSK GENMASK(15, 8) 43*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D2_MAC1_MSK GENMASK(7, 0) 44*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D2_MAC0_MSK GENMASK(15, 8) 45*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_VID_MSK GENMASK(11, 0) 46*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_IVL_MSK GENMASK(13, 13) 47*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_MBR_HI1_MSK GENMASK(15, 14) 48*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_MBR_HI1_MSK GENMASK(9, 8) 49*336e3e4aSAlvin Šipraga 50*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D4_MBR_MSK GENMASK(7, 0) 51*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_MBR_LO_MSK GENMASK(7, 0) 52*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D4_IGMPIDX_MSK GENMASK(15, 8) 53*336e3e4aSAlvin Šipraga 54*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK GENMASK(0, 0) 55*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_PRIORITY_MSK GENMASK(3, 1) 56*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_FWD_PRI_MSK GENMASK(4, 4) 57*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_STATIC_MSK GENMASK(5, 5) 58*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_MBR_HI2_MSK GENMASK(7, 7) 59*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_MBR_HI2_MSK GENMASK(10, 10) 60*336e3e4aSAlvin Šipraga 61*336e3e4aSAlvin Šipraga /* Port flush command registers - writing a 1 to the port's MASK bit will 62*336e3e4aSAlvin Šipraga * initiate the flush procedure. Completion is signalled when the corresponding 63*336e3e4aSAlvin Šipraga * BUSY bit is 0. 64*336e3e4aSAlvin Šipraga */ 65*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_REG 0x0A36 66*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_MSK_MSK GENMASK(7, 0) 67*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_BUSY_MSK GENMASK(15, 8) 68*336e3e4aSAlvin Šipraga 69*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_EXT_REG 0x0A35 70*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK GENMASK(2, 0) 71*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK GENMASK(5, 3) 72*336e3e4aSAlvin Šipraga 73*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL1_REG 0x0A37 74*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL1_VID_MSK GENMASK(11, 0) 75*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL1_FID_MSK GENMASK(15, 12) 76*336e3e4aSAlvin Šipraga 77*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_REG 0x0A38 78*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK GENMASK(1, 0) 79*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT 0 80*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID 1 81*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_FID 2 82*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK GENMASK(2, 2) 83*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC 0 84*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_TYPE_BOTH 1 85*336e3e4aSAlvin Šipraga 86*336e3e4aSAlvin Šipraga /* This flushes the entire LUT, reading it back it will turn 0 when the 87*336e3e4aSAlvin Šipraga * operation is complete 88*336e3e4aSAlvin Šipraga */ 89*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL3_REG 0x0A39 90*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL3_MSK GENMASK(0, 0) 91*336e3e4aSAlvin Šipraga 92*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc_key { 93*336e3e4aSAlvin Šipraga u8 mac_addr[ETH_ALEN]; 94*336e3e4aSAlvin Šipraga u16 vid; 95*336e3e4aSAlvin Šipraga u16 fid; 96*336e3e4aSAlvin Šipraga bool ivl; 97*336e3e4aSAlvin Šipraga u16 efid; 98*336e3e4aSAlvin Šipraga }; 99*336e3e4aSAlvin Šipraga 100*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc { 101*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc_key key; 102*336e3e4aSAlvin Šipraga u8 port; 103*336e3e4aSAlvin Šipraga u8 age; 104*336e3e4aSAlvin Šipraga u8 priority; 105*336e3e4aSAlvin Šipraga 106*336e3e4aSAlvin Šipraga bool sa_block; 107*336e3e4aSAlvin Šipraga bool da_block; 108*336e3e4aSAlvin Šipraga bool auth; 109*336e3e4aSAlvin Šipraga bool is_static; 110*336e3e4aSAlvin Šipraga bool sa_pri; 111*336e3e4aSAlvin Šipraga bool fwd_pri; 112*336e3e4aSAlvin Šipraga }; 113*336e3e4aSAlvin Šipraga 114*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc_key { 115*336e3e4aSAlvin Šipraga u8 mac_addr[ETH_ALEN]; 116*336e3e4aSAlvin Šipraga union { 117*336e3e4aSAlvin Šipraga u16 vid; /* IVL */ 118*336e3e4aSAlvin Šipraga u16 fid; /* SVL */ 119*336e3e4aSAlvin Šipraga }; 120*336e3e4aSAlvin Šipraga bool ivl; 121*336e3e4aSAlvin Šipraga }; 122*336e3e4aSAlvin Šipraga 123*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc { 124*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc_key key; 125*336e3e4aSAlvin Šipraga u16 member; 126*336e3e4aSAlvin Šipraga u8 priority; 127*336e3e4aSAlvin Šipraga u8 igmpidx; 128*336e3e4aSAlvin Šipraga 129*336e3e4aSAlvin Šipraga bool is_static; 130*336e3e4aSAlvin Šipraga bool fwd_pri; 131*336e3e4aSAlvin Šipraga bool igmp_asic; 132*336e3e4aSAlvin Šipraga }; 133*336e3e4aSAlvin Šipraga 134*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_uc *uc) 135*336e3e4aSAlvin Šipraga { 136*336e3e4aSAlvin Šipraga u32 val; 137*336e3e4aSAlvin Šipraga 138*336e3e4aSAlvin Šipraga uc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC5_MSK, data[0]); 139*336e3e4aSAlvin Šipraga uc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC4_MSK, data[0]); 140*336e3e4aSAlvin Šipraga uc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC3_MSK, data[1]); 141*336e3e4aSAlvin Šipraga uc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC2_MSK, data[1]); 142*336e3e4aSAlvin Šipraga uc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC1_MSK, data[2]); 143*336e3e4aSAlvin Šipraga uc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC0_MSK, data[2]); 144*336e3e4aSAlvin Šipraga uc->key.efid = FIELD_GET(RTL8365MB_L2_UC_D4_EFID_MSK, data[4]); 145*336e3e4aSAlvin Šipraga uc->key.vid = FIELD_GET(RTL8365MB_L2_UC_D3_VID_MSK, data[3]); 146*336e3e4aSAlvin Šipraga uc->key.ivl = FIELD_GET(RTL8365MB_L2_UC_D3_IVL_MSK, data[3]); 147*336e3e4aSAlvin Šipraga uc->key.fid = FIELD_GET(RTL8365MB_L2_UC_D4_FID_MSK, data[4]); 148*336e3e4aSAlvin Šipraga uc->age = FIELD_GET(RTL8365MB_L2_UC_D4_AGE_MSK, data[4]); 149*336e3e4aSAlvin Šipraga uc->auth = FIELD_GET(RTL8365MB_L2_UC_D4_AUTH_MSK, data[4]); 150*336e3e4aSAlvin Šipraga 151*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_UC_D4_PORT_MSK, data[4]); 152*336e3e4aSAlvin Šipraga uc->port = FIELD_PREP(RTL8365MB_L2_UC_PORT_LO_MSK, val); 153*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, data[3]); 154*336e3e4aSAlvin Šipraga uc->port |= FIELD_PREP(RTL8365MB_L2_UC_PORT_HI_MSK, val); 155*336e3e4aSAlvin Šipraga 156*336e3e4aSAlvin Šipraga uc->sa_pri = FIELD_GET(RTL8365MB_L2_UC_D4_SA_PRI_MSK, data[4]); 157*336e3e4aSAlvin Šipraga uc->fwd_pri = FIELD_GET(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, data[5]); 158*336e3e4aSAlvin Šipraga uc->sa_block = FIELD_GET(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, data[4]); 159*336e3e4aSAlvin Šipraga uc->da_block = FIELD_GET(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, data[5]); 160*336e3e4aSAlvin Šipraga uc->priority = FIELD_GET(RTL8365MB_L2_UC_D5_PRIORITY_MSK, data[5]); 161*336e3e4aSAlvin Šipraga uc->is_static = FIELD_GET(RTL8365MB_L2_UC_D5_STATIC_MSK, data[5]); 162*336e3e4aSAlvin Šipraga } 163*336e3e4aSAlvin Šipraga 164*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 *data) 165*336e3e4aSAlvin Šipraga { 166*336e3e4aSAlvin Šipraga u32 val; 167*336e3e4aSAlvin Šipraga 168*336e3e4aSAlvin Šipraga memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2); 169*336e3e4aSAlvin Šipraga data[0] |= 170*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D0_MAC5_MSK, uc->key.mac_addr[5]); 171*336e3e4aSAlvin Šipraga data[0] |= 172*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D0_MAC4_MSK, uc->key.mac_addr[4]); 173*336e3e4aSAlvin Šipraga data[1] |= 174*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D1_MAC3_MSK, uc->key.mac_addr[3]); 175*336e3e4aSAlvin Šipraga data[1] |= 176*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D1_MAC2_MSK, uc->key.mac_addr[2]); 177*336e3e4aSAlvin Šipraga data[2] |= 178*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D2_MAC1_MSK, uc->key.mac_addr[1]); 179*336e3e4aSAlvin Šipraga data[2] |= 180*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_UC_D2_MAC0_MSK, uc->key.mac_addr[0]); 181*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_VID_MSK, uc->key.vid); 182*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_IVL_MSK, uc->key.ivl); 183*336e3e4aSAlvin Šipraga 184*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_UC_PORT_HI_MSK, uc->port); 185*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, val); 186*336e3e4aSAlvin Šipraga 187*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_FID_MSK, uc->key.fid); 188*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_EFID_MSK, uc->key.efid); 189*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AGE_MSK, uc->age); 190*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AUTH_MSK, uc->auth); 191*336e3e4aSAlvin Šipraga 192*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_UC_PORT_LO_MSK, uc->port); 193*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_PORT_MSK, val); 194*336e3e4aSAlvin Šipraga 195*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_PRI_MSK, uc->sa_pri); 196*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, uc->sa_block); 197*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, uc->fwd_pri); 198*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, uc->da_block); 199*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_PRIORITY_MSK, uc->priority); 200*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_STATIC_MSK, uc->is_static); 201*336e3e4aSAlvin Šipraga } 202*336e3e4aSAlvin Šipraga 203*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_mc *mc) 204*336e3e4aSAlvin Šipraga { 205*336e3e4aSAlvin Šipraga u32 val; 206*336e3e4aSAlvin Šipraga 207*336e3e4aSAlvin Šipraga mc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC5_MSK, data[0]); 208*336e3e4aSAlvin Šipraga mc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC4_MSK, data[0]); 209*336e3e4aSAlvin Šipraga mc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC3_MSK, data[1]); 210*336e3e4aSAlvin Šipraga mc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC2_MSK, data[1]); 211*336e3e4aSAlvin Šipraga mc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC1_MSK, data[2]); 212*336e3e4aSAlvin Šipraga mc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC0_MSK, data[2]); 213*336e3e4aSAlvin Šipraga /* key.vid,key.fid shares the same memory space */ 214*336e3e4aSAlvin Šipraga mc->key.vid = FIELD_GET(RTL8365MB_L2_MC_D3_VID_MSK, data[3]); 215*336e3e4aSAlvin Šipraga mc->key.ivl = FIELD_GET(RTL8365MB_L2_MC_D3_IVL_MSK, data[3]); 216*336e3e4aSAlvin Šipraga mc->priority = FIELD_GET(RTL8365MB_L2_MC_D5_PRIORITY_MSK, data[5]); 217*336e3e4aSAlvin Šipraga mc->fwd_pri = FIELD_GET(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, data[5]); 218*336e3e4aSAlvin Šipraga mc->is_static = FIELD_GET(RTL8365MB_L2_MC_D5_STATIC_MSK, data[5]); 219*336e3e4aSAlvin Šipraga 220*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_D4_MBR_MSK, data[4]); 221*336e3e4aSAlvin Šipraga mc->member = FIELD_PREP(RTL8365MB_L2_MC_MBR_LO_MSK, val); 222*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, data[3]); 223*336e3e4aSAlvin Šipraga mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI1_MSK, val); 224*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, data[5]); 225*336e3e4aSAlvin Šipraga mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI2_MSK, val); 226*336e3e4aSAlvin Šipraga 227*336e3e4aSAlvin Šipraga mc->igmpidx = FIELD_GET(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, data[4]); 228*336e3e4aSAlvin Šipraga mc->igmp_asic = FIELD_GET(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, data[5]); 229*336e3e4aSAlvin Šipraga } 230*336e3e4aSAlvin Šipraga 231*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 *data) 232*336e3e4aSAlvin Šipraga { 233*336e3e4aSAlvin Šipraga u32 val; 234*336e3e4aSAlvin Šipraga 235*336e3e4aSAlvin Šipraga memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2); 236*336e3e4aSAlvin Šipraga data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC5_MSK, mc->key.mac_addr[5]); 237*336e3e4aSAlvin Šipraga data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC4_MSK, mc->key.mac_addr[4]); 238*336e3e4aSAlvin Šipraga data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC3_MSK, mc->key.mac_addr[3]); 239*336e3e4aSAlvin Šipraga data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC2_MSK, mc->key.mac_addr[2]); 240*336e3e4aSAlvin Šipraga data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC1_MSK, mc->key.mac_addr[1]); 241*336e3e4aSAlvin Šipraga data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC0_MSK, mc->key.mac_addr[0]); 242*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_VID_MSK, mc->key.vid); 243*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_IVL_MSK, mc->key.ivl); 244*336e3e4aSAlvin Šipraga 245*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI1_MSK, mc->member); 246*336e3e4aSAlvin Šipraga data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, val); 247*336e3e4aSAlvin Šipraga 248*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_MBR_LO_MSK, mc->member); 249*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_MBR_MSK, val); 250*336e3e4aSAlvin Šipraga 251*336e3e4aSAlvin Šipraga data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, mc->igmpidx); 252*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, mc->igmp_asic); 253*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_PRIORITY_MSK, mc->priority); 254*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, mc->fwd_pri); 255*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_STATIC_MSK, mc->is_static); 256*336e3e4aSAlvin Šipraga 257*336e3e4aSAlvin Šipraga val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI2_MSK, mc->member); 258*336e3e4aSAlvin Šipraga data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, val); 259*336e3e4aSAlvin Šipraga } 260*336e3e4aSAlvin Šipraga 261*336e3e4aSAlvin Šipraga /* 262*336e3e4aSAlvin Šipraga * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry 263*336e3e4aSAlvin Šipraga * @priv: realtek_priv pointer 264*336e3e4aSAlvin Šipraga * @addr: as input, the table index to start the walk 265*336e3e4aSAlvin Šipraga * as output, the found table index 266*336e3e4aSAlvin Šipraga * @port: restrict the walk on entries related to port 267*336e3e4aSAlvin Šipraga * @entry: returned L2 Unicast table entry 268*336e3e4aSAlvin Šipraga * 269*336e3e4aSAlvin Šipraga * This function gets the next unicast L2 table entry starting from @addr 270*336e3e4aSAlvin Šipraga * and checking exclusively entries related to @port. 271*336e3e4aSAlvin Šipraga * 272*336e3e4aSAlvin Šipraga * On success, it returns 0, updates @addr to the index of the found entry, 273*336e3e4aSAlvin Šipraga * and populates @entry. If the search reaches the end of the table and 274*336e3e4aSAlvin Šipraga * wraps around and @addr will be strictly lower than the input @addr. 275*336e3e4aSAlvin Šipraga * Callers must detect this wrap-around condition to prevent infinite loops. 276*336e3e4aSAlvin Šipraga * 277*336e3e4aSAlvin Šipraga * If the table contains no matching entries at all, it returns -ENOENT 278*336e3e4aSAlvin Šipraga * and leaves @addr and @entry unmodified. 279*336e3e4aSAlvin Šipraga * 280*336e3e4aSAlvin Šipraga * Return: Returns 0 on success, a negative error on failure. 281*336e3e4aSAlvin Šipraga **/ 282*336e3e4aSAlvin Šipraga int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port, 283*336e3e4aSAlvin Šipraga struct realtek_fdb_entry *entry) 284*336e3e4aSAlvin Šipraga { 285*336e3e4aSAlvin Šipraga u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; 286*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc uc; 287*336e3e4aSAlvin Šipraga int ret; 288*336e3e4aSAlvin Šipraga 289*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 290*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_READ, addr, 291*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, 292*336e3e4aSAlvin Šipraga port, data, RTL8365MB_L2_ENTRY_SIZE); 293*336e3e4aSAlvin Šipraga if (ret) 294*336e3e4aSAlvin Šipraga return ret; 295*336e3e4aSAlvin Šipraga 296*336e3e4aSAlvin Šipraga rtl8365mb_l2_data_to_uc(data, &uc); 297*336e3e4aSAlvin Šipraga 298*336e3e4aSAlvin Šipraga ether_addr_copy(entry->mac_addr, uc.key.mac_addr); 299*336e3e4aSAlvin Šipraga entry->vid = uc.key.vid; 300*336e3e4aSAlvin Šipraga entry->is_static = uc.is_static; 301*336e3e4aSAlvin Šipraga 302*336e3e4aSAlvin Šipraga return 0; 303*336e3e4aSAlvin Šipraga } 304*336e3e4aSAlvin Šipraga 305*336e3e4aSAlvin Šipraga int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port, 306*336e3e4aSAlvin Šipraga const unsigned char mac_addr[static ETH_ALEN], 307*336e3e4aSAlvin Šipraga u16 efid, u16 vid) 308*336e3e4aSAlvin Šipraga { 309*336e3e4aSAlvin Šipraga u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; 310*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc uc = { 0 }; 311*336e3e4aSAlvin Šipraga u16 addr; 312*336e3e4aSAlvin Šipraga int ret; 313*336e3e4aSAlvin Šipraga 314*336e3e4aSAlvin Šipraga memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); 315*336e3e4aSAlvin Šipraga uc.key.efid = efid; 316*336e3e4aSAlvin Šipraga uc.key.fid = 0; 317*336e3e4aSAlvin Šipraga uc.key.ivl = true; 318*336e3e4aSAlvin Šipraga uc.key.vid = vid; 319*336e3e4aSAlvin Šipraga 320*336e3e4aSAlvin Šipraga uc.port = port; 321*336e3e4aSAlvin Šipraga /* Entries programmed by DSA (including those dynamically learned by 322*336e3e4aSAlvin Šipraga * the software bridge and injected into the CPU port via assisted 323*336e3e4aSAlvin Šipraga * learning) must be static. We do not let HW decrease age behind the 324*336e3e4aSAlvin Šipraga * OS's back. As a trade-off, these will show up as permanent to users. 325*336e3e4aSAlvin Šipraga */ 326*336e3e4aSAlvin Šipraga uc.is_static = true; 327*336e3e4aSAlvin Šipraga /* age greater than 0 adds/updates entries */ 328*336e3e4aSAlvin Šipraga uc.age = 1; 329*336e3e4aSAlvin Šipraga rtl8365mb_l2_uc_to_data(&uc, data); 330*336e3e4aSAlvin Šipraga 331*336e3e4aSAlvin Šipraga /* add the new entry or update an existing one */ 332*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 333*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_WRITE, &addr, 334*336e3e4aSAlvin Šipraga 0, 0, 335*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 336*336e3e4aSAlvin Šipraga 337*336e3e4aSAlvin Šipraga /* Assume the missing new entry as the table is full */ 338*336e3e4aSAlvin Šipraga if (ret == -ENOENT) 339*336e3e4aSAlvin Šipraga return -ENOSPC; 340*336e3e4aSAlvin Šipraga 341*336e3e4aSAlvin Šipraga /* addr will hold the table index, but it is not used here */ 342*336e3e4aSAlvin Šipraga return ret; 343*336e3e4aSAlvin Šipraga } 344*336e3e4aSAlvin Šipraga 345*336e3e4aSAlvin Šipraga int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port, 346*336e3e4aSAlvin Šipraga const unsigned char mac_addr[static ETH_ALEN], 347*336e3e4aSAlvin Šipraga u16 efid, u16 vid) 348*336e3e4aSAlvin Šipraga { 349*336e3e4aSAlvin Šipraga u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; 350*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc uc = { 0 }; 351*336e3e4aSAlvin Šipraga u16 addr; 352*336e3e4aSAlvin Šipraga int ret; 353*336e3e4aSAlvin Šipraga 354*336e3e4aSAlvin Šipraga memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); 355*336e3e4aSAlvin Šipraga uc.key.efid = efid; 356*336e3e4aSAlvin Šipraga uc.key.fid = 0; 357*336e3e4aSAlvin Šipraga uc.key.ivl = true; 358*336e3e4aSAlvin Šipraga uc.key.vid = vid; 359*336e3e4aSAlvin Šipraga /* age 0 deletes the entry */ 360*336e3e4aSAlvin Šipraga uc.age = 0; 361*336e3e4aSAlvin Šipraga rtl8365mb_l2_uc_to_data(&uc, data); 362*336e3e4aSAlvin Šipraga 363*336e3e4aSAlvin Šipraga /* it looks like the switch will always add/update the entry, 364*336e3e4aSAlvin Šipraga * even when age is 0 or uc.key did not match an existing entry, 365*336e3e4aSAlvin Šipraga * just to immediately drop it because age is zero. You can still 366*336e3e4aSAlvin Šipraga * get the added/updated address from @addr 367*336e3e4aSAlvin Šipraga */ 368*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 369*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_WRITE, &addr, 370*336e3e4aSAlvin Šipraga 0, 0, 371*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 372*336e3e4aSAlvin Šipraga 373*336e3e4aSAlvin Šipraga if (ret == -ENOENT) { 374*336e3e4aSAlvin Šipraga dev_dbg(priv->dev, "%s: %pM vid=%d efid=%d missing\n", 375*336e3e4aSAlvin Šipraga __func__, mac_addr, vid, efid); 376*336e3e4aSAlvin Šipraga /* Silently return success */ 377*336e3e4aSAlvin Šipraga return 0; 378*336e3e4aSAlvin Šipraga } 379*336e3e4aSAlvin Šipraga 380*336e3e4aSAlvin Šipraga /* addr will hold the table index, but it is not used here */ 381*336e3e4aSAlvin Šipraga return ret; 382*336e3e4aSAlvin Šipraga } 383*336e3e4aSAlvin Šipraga 384*336e3e4aSAlvin Šipraga int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid) 385*336e3e4aSAlvin Šipraga { 386*336e3e4aSAlvin Šipraga int mode = vid ? RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID : 387*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT; 388*336e3e4aSAlvin Šipraga u32 val, mask; 389*336e3e4aSAlvin Šipraga int ret; 390*336e3e4aSAlvin Šipraga 391*336e3e4aSAlvin Šipraga mutex_lock(&priv->map_lock); 392*336e3e4aSAlvin Šipraga 393*336e3e4aSAlvin Šipraga /* Configure flushing mode; only flush dynamic entries */ 394*336e3e4aSAlvin Šipraga ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL2_REG, 395*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK, 396*336e3e4aSAlvin Šipraga mode) | 397*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK, 398*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC)); 399*336e3e4aSAlvin Šipraga if (ret) 400*336e3e4aSAlvin Šipraga goto out; 401*336e3e4aSAlvin Šipraga 402*336e3e4aSAlvin Šipraga ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL1_REG, 403*336e3e4aSAlvin Šipraga FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL1_VID_MSK, vid)); 404*336e3e4aSAlvin Šipraga 405*336e3e4aSAlvin Šipraga if (ret) 406*336e3e4aSAlvin Šipraga goto out; 407*336e3e4aSAlvin Šipraga /* Now issue the flush command and wait for its completion. There are 408*336e3e4aSAlvin Šipraga * two registers for this purpose, and which one to use depends on the 409*336e3e4aSAlvin Šipraga * port number. The _EXT register is for ports 8 or higher. 410*336e3e4aSAlvin Šipraga */ 411*336e3e4aSAlvin Šipraga if (port < 8) { 412*336e3e4aSAlvin Šipraga val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_MSK_MSK, 413*336e3e4aSAlvin Šipraga BIT(port) & 0xFF); 414*336e3e4aSAlvin Šipraga ret = regmap_write(priv->map_nolock, 415*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_PORT_REG, val); 416*336e3e4aSAlvin Šipraga if (ret) 417*336e3e4aSAlvin Šipraga goto out; 418*336e3e4aSAlvin Šipraga 419*336e3e4aSAlvin Šipraga mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_BUSY_MSK, 420*336e3e4aSAlvin Šipraga BIT(port) & 0xFF); 421*336e3e4aSAlvin Šipraga ret = regmap_read_poll_timeout(priv->map_nolock, 422*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_PORT_REG, 423*336e3e4aSAlvin Šipraga val, !(val & mask), 10, 10000); 424*336e3e4aSAlvin Šipraga if (ret) 425*336e3e4aSAlvin Šipraga goto out; 426*336e3e4aSAlvin Šipraga } else { 427*336e3e4aSAlvin Šipraga val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK, 428*336e3e4aSAlvin Šipraga BIT(port) >> 8); 429*336e3e4aSAlvin Šipraga ret = regmap_write(priv->map_nolock, 430*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_PORT_EXT_REG, val); 431*336e3e4aSAlvin Šipraga if (ret) 432*336e3e4aSAlvin Šipraga goto out; 433*336e3e4aSAlvin Šipraga 434*336e3e4aSAlvin Šipraga mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK, 435*336e3e4aSAlvin Šipraga BIT(port) >> 8); 436*336e3e4aSAlvin Šipraga ret = regmap_read_poll_timeout(priv->map_nolock, 437*336e3e4aSAlvin Šipraga RTL8365MB_L2_FLUSH_PORT_EXT_REG, 438*336e3e4aSAlvin Šipraga val, !(val & mask), 10, 10000); 439*336e3e4aSAlvin Šipraga if (ret) 440*336e3e4aSAlvin Šipraga goto out; 441*336e3e4aSAlvin Šipraga } 442*336e3e4aSAlvin Šipraga 443*336e3e4aSAlvin Šipraga out: 444*336e3e4aSAlvin Šipraga mutex_unlock(&priv->map_lock); 445*336e3e4aSAlvin Šipraga 446*336e3e4aSAlvin Šipraga return ret; 447*336e3e4aSAlvin Šipraga } 448*336e3e4aSAlvin Šipraga 449*336e3e4aSAlvin Šipraga int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port, 450*336e3e4aSAlvin Šipraga const unsigned char mac_addr[static ETH_ALEN], 451*336e3e4aSAlvin Šipraga u16 vid) 452*336e3e4aSAlvin Šipraga { 453*336e3e4aSAlvin Šipraga u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; 454*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc mc = { 0 }; 455*336e3e4aSAlvin Šipraga u16 addr; 456*336e3e4aSAlvin Šipraga int ret; 457*336e3e4aSAlvin Šipraga 458*336e3e4aSAlvin Šipraga memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); 459*336e3e4aSAlvin Šipraga mc.key.vid = vid; 460*336e3e4aSAlvin Šipraga mc.key.ivl = true; 461*336e3e4aSAlvin Šipraga /* Already set the port and is_static, although not used in OP_READ, 462*336e3e4aSAlvin Šipraga * data will be ready for OP_WRITE if it is a new entry. 463*336e3e4aSAlvin Šipraga */ 464*336e3e4aSAlvin Šipraga mc.member |= BIT(port); 465*336e3e4aSAlvin Šipraga mc.is_static = 1; 466*336e3e4aSAlvin Šipraga rtl8365mb_l2_mc_to_data(&mc, data); 467*336e3e4aSAlvin Šipraga 468*336e3e4aSAlvin Šipraga /* First look for an existing entry (to get existing port members) */ 469*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 470*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_READ, &addr, 471*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_L2_METHOD_MAC, 0, 472*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 473*336e3e4aSAlvin Šipraga if (!ret) { 474*336e3e4aSAlvin Šipraga /* There is already an entry... */ 475*336e3e4aSAlvin Šipraga rtl8365mb_l2_data_to_mc(data, &mc); 476*336e3e4aSAlvin Šipraga dev_dbg(priv->dev, 477*336e3e4aSAlvin Šipraga "%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n", 478*336e3e4aSAlvin Šipraga __func__, mac_addr, addr, mc.member, mc.igmpidx, 479*336e3e4aSAlvin Šipraga mc.is_static ? "static" : "dynamic"); 480*336e3e4aSAlvin Šipraga /* the port must be added as a member */ 481*336e3e4aSAlvin Šipraga mc.member |= BIT(port); 482*336e3e4aSAlvin Šipraga 483*336e3e4aSAlvin Šipraga if (!mc.is_static) { 484*336e3e4aSAlvin Šipraga dev_dbg(priv->dev, 485*336e3e4aSAlvin Šipraga "%s: promoting addr=%d group to static\n", 486*336e3e4aSAlvin Šipraga __func__, addr); 487*336e3e4aSAlvin Šipraga mc.is_static = 1; 488*336e3e4aSAlvin Šipraga } 489*336e3e4aSAlvin Šipraga 490*336e3e4aSAlvin Šipraga rtl8365mb_l2_mc_to_data(&mc, data); 491*336e3e4aSAlvin Šipraga } else if (ret == -ENOENT) { 492*336e3e4aSAlvin Šipraga /* New entry, no need to update data again as it already 493*336e3e4aSAlvin Šipraga * includes the member. 494*336e3e4aSAlvin Šipraga * 495*336e3e4aSAlvin Šipraga * Multicast hardware entries do not support EFID (bridge 496*336e3e4aSAlvin Šipraga * isolation). However, traffic isolation is still maintained 497*336e3e4aSAlvin Šipraga * because the hardware applies the port isolation masks 498*336e3e4aSAlvin Šipraga * (pmasks) configured in bridge_join after the L2 lookup. 499*336e3e4aSAlvin Šipraga * Entries from different bridges will collide on the same 500*336e3e4aSAlvin Šipraga * MAC+VID slot with an OR'ed member mask, but packets will 501*336e3e4aSAlvin Šipraga * only exit through ports allowed by the source port's pmask. 502*336e3e4aSAlvin Šipraga */ 503*336e3e4aSAlvin Šipraga } else { 504*336e3e4aSAlvin Šipraga return ret; 505*336e3e4aSAlvin Šipraga } 506*336e3e4aSAlvin Šipraga 507*336e3e4aSAlvin Šipraga /* add the new entry or update an existing one */ 508*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 509*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_WRITE, &addr, 510*336e3e4aSAlvin Šipraga 0, 0, 511*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 512*336e3e4aSAlvin Šipraga 513*336e3e4aSAlvin Šipraga /* Assume the missing new entry as the table is full */ 514*336e3e4aSAlvin Šipraga if (ret == -ENOENT) 515*336e3e4aSAlvin Šipraga return -ENOSPC; 516*336e3e4aSAlvin Šipraga 517*336e3e4aSAlvin Šipraga return ret; 518*336e3e4aSAlvin Šipraga } 519*336e3e4aSAlvin Šipraga 520*336e3e4aSAlvin Šipraga int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port, 521*336e3e4aSAlvin Šipraga const unsigned char mac_addr[static ETH_ALEN], 522*336e3e4aSAlvin Šipraga u16 vid) 523*336e3e4aSAlvin Šipraga { 524*336e3e4aSAlvin Šipraga u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; 525*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc mc = { 0 }; 526*336e3e4aSAlvin Šipraga u16 addr; 527*336e3e4aSAlvin Šipraga int ret; 528*336e3e4aSAlvin Šipraga 529*336e3e4aSAlvin Šipraga memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); 530*336e3e4aSAlvin Šipraga mc.key.vid = vid; 531*336e3e4aSAlvin Šipraga mc.key.ivl = true; 532*336e3e4aSAlvin Šipraga rtl8365mb_l2_mc_to_data(&mc, data); 533*336e3e4aSAlvin Šipraga 534*336e3e4aSAlvin Šipraga /* First look for an existing entry (to get existing port members) */ 535*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 536*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_READ, &addr, 537*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_L2_METHOD_MAC, 0, 538*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 539*336e3e4aSAlvin Šipraga if (ret == -ENOENT) { 540*336e3e4aSAlvin Šipraga dev_dbg(priv->dev, "%s: %pM vid=%d missing\n", 541*336e3e4aSAlvin Šipraga __func__, mac_addr, vid); 542*336e3e4aSAlvin Šipraga /* Silently return success */ 543*336e3e4aSAlvin Šipraga return 0; 544*336e3e4aSAlvin Šipraga } 545*336e3e4aSAlvin Šipraga 546*336e3e4aSAlvin Šipraga if (ret) 547*336e3e4aSAlvin Šipraga /* Return on any other error */ 548*336e3e4aSAlvin Šipraga return ret; 549*336e3e4aSAlvin Šipraga 550*336e3e4aSAlvin Šipraga rtl8365mb_l2_data_to_mc(data, &mc); 551*336e3e4aSAlvin Šipraga dev_dbg(priv->dev, 552*336e3e4aSAlvin Šipraga "%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n", 553*336e3e4aSAlvin Šipraga __func__, mac_addr, addr, mc.member, mc.igmpidx, 554*336e3e4aSAlvin Šipraga mc.is_static ? "static" : "dynamic"); 555*336e3e4aSAlvin Šipraga /* the port must be removed as a member */ 556*336e3e4aSAlvin Šipraga mc.member &= ~BIT(port); 557*336e3e4aSAlvin Šipraga if (!mc.member) { 558*336e3e4aSAlvin Šipraga /* Multicast entries do not have an age field. Clearing both 559*336e3e4aSAlvin Šipraga * the member portmask and is_static flags is the hardware 560*336e3e4aSAlvin Šipraga * signal to invalidate and reclaim the L2 table slot. 561*336e3e4aSAlvin Šipraga */ 562*336e3e4aSAlvin Šipraga mc.is_static = 0; 563*336e3e4aSAlvin Šipraga mc.igmpidx = 0; 564*336e3e4aSAlvin Šipraga mc.priority = 0; 565*336e3e4aSAlvin Šipraga mc.fwd_pri = 0; 566*336e3e4aSAlvin Šipraga mc.igmp_asic = 0; 567*336e3e4aSAlvin Šipraga } 568*336e3e4aSAlvin Šipraga rtl8365mb_l2_mc_to_data(&mc, data); 569*336e3e4aSAlvin Šipraga 570*336e3e4aSAlvin Šipraga /* update the existing entry. */ 571*336e3e4aSAlvin Šipraga ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, 572*336e3e4aSAlvin Šipraga RTL8365MB_TABLE_OP_WRITE, &addr, 573*336e3e4aSAlvin Šipraga 0, 0, 574*336e3e4aSAlvin Šipraga data, RTL8365MB_L2_ENTRY_SIZE); 575*336e3e4aSAlvin Šipraga return ret; 576*336e3e4aSAlvin Šipraga } 577