18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Realtek SMI library helpers for the RTL8366x variants 38c2ecf20Sopenharmony_ci * RTL8366RB and RTL8366S 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 68c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> 98c2ecf20Sopenharmony_ci * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 128c2ecf20Sopenharmony_ci#include <net/dsa.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "realtek-smi-core.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciint rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci int ret; 198c2ecf20Sopenharmony_ci int i; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci *used = 0; 228c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_ports; i++) { 238c2ecf20Sopenharmony_ci int index = 0; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci ret = smi->ops->get_mc_index(smi, i, &index); 268c2ecf20Sopenharmony_ci if (ret) 278c2ecf20Sopenharmony_ci return ret; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (mc_index == index) { 308c2ecf20Sopenharmony_ci *used = 1; 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_mc_is_used); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration 418c2ecf20Sopenharmony_ci * @smi: the Realtek SMI device instance 428c2ecf20Sopenharmony_ci * @vid: the VLAN ID to look up or allocate 438c2ecf20Sopenharmony_ci * @vlanmc: the pointer will be assigned to a pointer to a valid member config 448c2ecf20Sopenharmony_ci * if successful 458c2ecf20Sopenharmony_ci * @return: index of a new member config or negative error number 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_cistatic int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, 488c2ecf20Sopenharmony_ci struct rtl8366_vlan_mc *vlanmc) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct rtl8366_vlan_4k vlan4k; 518c2ecf20Sopenharmony_ci int ret; 528c2ecf20Sopenharmony_ci int i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Try to find an existing member config entry for this VID */ 558c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_vlan_mc; i++) { 568c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_mc(smi, i, vlanmc); 578c2ecf20Sopenharmony_ci if (ret) { 588c2ecf20Sopenharmony_ci dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", 598c2ecf20Sopenharmony_ci i, vid); 608c2ecf20Sopenharmony_ci return ret; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (vid == vlanmc->vid) 648c2ecf20Sopenharmony_ci return i; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* We have no MC entry for this VID, try to find an empty one */ 688c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_vlan_mc; i++) { 698c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_mc(smi, i, vlanmc); 708c2ecf20Sopenharmony_ci if (ret) { 718c2ecf20Sopenharmony_ci dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", 728c2ecf20Sopenharmony_ci i, vid); 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (vlanmc->vid == 0 && vlanmc->member == 0) { 778c2ecf20Sopenharmony_ci /* Update the entry from the 4K table */ 788c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); 798c2ecf20Sopenharmony_ci if (ret) { 808c2ecf20Sopenharmony_ci dev_err(smi->dev, "error looking for 4K VLAN MC %d for VID %d\n", 818c2ecf20Sopenharmony_ci i, vid); 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci vlanmc->vid = vid; 868c2ecf20Sopenharmony_ci vlanmc->member = vlan4k.member; 878c2ecf20Sopenharmony_ci vlanmc->untag = vlan4k.untag; 888c2ecf20Sopenharmony_ci vlanmc->fid = vlan4k.fid; 898c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_mc(smi, i, vlanmc); 908c2ecf20Sopenharmony_ci if (ret) { 918c2ecf20Sopenharmony_ci dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", 928c2ecf20Sopenharmony_ci i, vid); 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci dev_dbg(smi->dev, "created new MC at index %d for VID %d\n", 978c2ecf20Sopenharmony_ci i, vid); 988c2ecf20Sopenharmony_ci return i; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* MC table is full, try to find an unused entry and replace it */ 1038c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_vlan_mc; i++) { 1048c2ecf20Sopenharmony_ci int used; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = rtl8366_mc_is_used(smi, i, &used); 1078c2ecf20Sopenharmony_ci if (ret) 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!used) { 1118c2ecf20Sopenharmony_ci /* Update the entry from the 4K table */ 1128c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); 1138c2ecf20Sopenharmony_ci if (ret) 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci vlanmc->vid = vid; 1178c2ecf20Sopenharmony_ci vlanmc->member = vlan4k.member; 1188c2ecf20Sopenharmony_ci vlanmc->untag = vlan4k.untag; 1198c2ecf20Sopenharmony_ci vlanmc->fid = vlan4k.fid; 1208c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_mc(smi, i, vlanmc); 1218c2ecf20Sopenharmony_ci if (ret) { 1228c2ecf20Sopenharmony_ci dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", 1238c2ecf20Sopenharmony_ci i, vid); 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci dev_dbg(smi->dev, "recycled MC at index %i for VID %d\n", 1278c2ecf20Sopenharmony_ci i, vid); 1288c2ecf20Sopenharmony_ci return i; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dev_err(smi->dev, "all VLAN member configurations are in use\n"); 1338c2ecf20Sopenharmony_ci return -ENOSPC; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member, 1378c2ecf20Sopenharmony_ci u32 untag, u32 fid) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct rtl8366_vlan_mc vlanmc; 1408c2ecf20Sopenharmony_ci struct rtl8366_vlan_4k vlan4k; 1418c2ecf20Sopenharmony_ci int mc; 1428c2ecf20Sopenharmony_ci int ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!smi->ops->is_vlan_valid(smi, vid)) 1458c2ecf20Sopenharmony_ci return -EINVAL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dev_dbg(smi->dev, 1488c2ecf20Sopenharmony_ci "setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", 1498c2ecf20Sopenharmony_ci vid, member, untag); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Update the 4K table */ 1528c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); 1538c2ecf20Sopenharmony_ci if (ret) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci vlan4k.member |= member; 1578c2ecf20Sopenharmony_ci vlan4k.untag |= untag; 1588c2ecf20Sopenharmony_ci vlan4k.fid = fid; 1598c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_4k(smi, &vlan4k); 1608c2ecf20Sopenharmony_ci if (ret) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(smi->dev, 1648c2ecf20Sopenharmony_ci "resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", 1658c2ecf20Sopenharmony_ci vid, vlan4k.member, vlan4k.untag); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Find or allocate a member config for this VID */ 1688c2ecf20Sopenharmony_ci ret = rtl8366_obtain_mc(smi, vid, &vlanmc); 1698c2ecf20Sopenharmony_ci if (ret < 0) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci mc = ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Update the MC entry */ 1748c2ecf20Sopenharmony_ci vlanmc.member |= member; 1758c2ecf20Sopenharmony_ci vlanmc.untag |= untag; 1768c2ecf20Sopenharmony_ci vlanmc.fid = fid; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Commit updates to the MC entry */ 1798c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_mc(smi, mc, &vlanmc); 1808c2ecf20Sopenharmony_ci if (ret) 1818c2ecf20Sopenharmony_ci dev_err(smi->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", 1828c2ecf20Sopenharmony_ci mc, vid); 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci dev_dbg(smi->dev, 1858c2ecf20Sopenharmony_ci "resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", 1868c2ecf20Sopenharmony_ci vid, vlanmc.member, vlanmc.untag); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_set_vlan); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciint rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port, 1938c2ecf20Sopenharmony_ci unsigned int vid) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct rtl8366_vlan_mc vlanmc; 1968c2ecf20Sopenharmony_ci int mc; 1978c2ecf20Sopenharmony_ci int ret; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (!smi->ops->is_vlan_valid(smi, vid)) 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Find or allocate a member config for this VID */ 2038c2ecf20Sopenharmony_ci ret = rtl8366_obtain_mc(smi, vid, &vlanmc); 2048c2ecf20Sopenharmony_ci if (ret < 0) 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci mc = ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = smi->ops->set_mc_index(smi, port, mc); 2098c2ecf20Sopenharmony_ci if (ret) { 2108c2ecf20Sopenharmony_ci dev_err(smi->dev, "set PVID: failed to set MC index %d for port %d\n", 2118c2ecf20Sopenharmony_ci mc, port); 2128c2ecf20Sopenharmony_ci return ret; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci dev_dbg(smi->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", 2168c2ecf20Sopenharmony_ci port, vid, mc); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_set_pvid); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciint rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci int ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* To enable 4k VLAN, ordinary VLAN must be enabled first, 2278c2ecf20Sopenharmony_ci * but if we disable 4k VLAN it is fine to leave ordinary 2288c2ecf20Sopenharmony_ci * VLAN enabled. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci if (enable) { 2318c2ecf20Sopenharmony_ci /* Make sure VLAN is ON */ 2328c2ecf20Sopenharmony_ci ret = smi->ops->enable_vlan(smi, true); 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci smi->vlan_enabled = true; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = smi->ops->enable_vlan4k(smi, enable); 2408c2ecf20Sopenharmony_ci if (ret) 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci smi->vlan4k_enabled = enable; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciint rtl8366_enable_vlan(struct realtek_smi *smi, bool enable) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = smi->ops->enable_vlan(smi, enable); 2538c2ecf20Sopenharmony_ci if (ret) 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci smi->vlan_enabled = enable; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* If we turn VLAN off, make sure that we turn off 2598c2ecf20Sopenharmony_ci * 4k VLAN as well, if that happened to be on. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci if (!enable) { 2628c2ecf20Sopenharmony_ci smi->vlan4k_enabled = false; 2638c2ecf20Sopenharmony_ci ret = smi->ops->enable_vlan4k(smi, false); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_enable_vlan); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciint rtl8366_reset_vlan(struct realtek_smi *smi) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct rtl8366_vlan_mc vlanmc; 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci int i; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci rtl8366_enable_vlan(smi, false); 2778c2ecf20Sopenharmony_ci rtl8366_enable_vlan4k(smi, false); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Clear the 16 VLAN member configurations */ 2808c2ecf20Sopenharmony_ci vlanmc.vid = 0; 2818c2ecf20Sopenharmony_ci vlanmc.priority = 0; 2828c2ecf20Sopenharmony_ci vlanmc.member = 0; 2838c2ecf20Sopenharmony_ci vlanmc.untag = 0; 2848c2ecf20Sopenharmony_ci vlanmc.fid = 0; 2858c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_vlan_mc; i++) { 2868c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); 2878c2ecf20Sopenharmony_ci if (ret) 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_reset_vlan); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciint rtl8366_init_vlan(struct realtek_smi *smi) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int port; 2988c2ecf20Sopenharmony_ci int ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = rtl8366_reset_vlan(smi); 3018c2ecf20Sopenharmony_ci if (ret) 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Loop over the available ports, for each port, associate 3058c2ecf20Sopenharmony_ci * it with the VLAN (port+1) 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci for (port = 0; port < smi->num_ports; port++) { 3088c2ecf20Sopenharmony_ci u32 mask; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (port == smi->cpu_port) 3118c2ecf20Sopenharmony_ci /* For the CPU port, make all ports members of this 3128c2ecf20Sopenharmony_ci * VLAN. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci mask = GENMASK((int)smi->num_ports - 1, 0); 3158c2ecf20Sopenharmony_ci else 3168c2ecf20Sopenharmony_ci /* For all other ports, enable itself plus the 3178c2ecf20Sopenharmony_ci * CPU port. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci mask = BIT(port) | BIT(smi->cpu_port); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* For each port, set the port as member of VLAN (port+1) 3228c2ecf20Sopenharmony_ci * and untagged, except for the CPU port: the CPU port (5) is 3238c2ecf20Sopenharmony_ci * member of VLAN 6 and so are ALL the other ports as well. 3248c2ecf20Sopenharmony_ci * Use filter 0 (no filter). 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci dev_info(smi->dev, "VLAN%d port mask for port %d, %08x\n", 3278c2ecf20Sopenharmony_ci (port + 1), port, mask); 3288c2ecf20Sopenharmony_ci ret = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0); 3298c2ecf20Sopenharmony_ci if (ret) 3308c2ecf20Sopenharmony_ci return ret; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dev_info(smi->dev, "VLAN%d port %d, PVID set to %d\n", 3338c2ecf20Sopenharmony_ci (port + 1), port, (port + 1)); 3348c2ecf20Sopenharmony_ci ret = rtl8366_set_pvid(smi, port, (port + 1)); 3358c2ecf20Sopenharmony_ci if (ret) 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return rtl8366_enable_vlan(smi, true); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_init_vlan); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, 3448c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 3478c2ecf20Sopenharmony_ci struct rtl8366_vlan_4k vlan4k; 3488c2ecf20Sopenharmony_ci int ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Use VLAN nr port + 1 since VLAN0 is not valid */ 3518c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 3528c2ecf20Sopenharmony_ci if (!smi->ops->is_vlan_valid(smi, port + 1)) 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci dev_info(smi->dev, "%s filtering on port %d\n", 3598c2ecf20Sopenharmony_ci vlan_filtering ? "enable" : "disable", 3608c2ecf20Sopenharmony_ci port); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* TODO: 3638c2ecf20Sopenharmony_ci * The hardware support filter ID (FID) 0..7, I have no clue how to 3648c2ecf20Sopenharmony_ci * support this in the driver when the callback only says on/off. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_4k(smi, port + 1, &vlan4k); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Just set the filter to FID 1 for now then */ 3718c2ecf20Sopenharmony_ci ret = rtl8366_set_vlan(smi, port + 1, 3728c2ecf20Sopenharmony_ci vlan4k.member, 3738c2ecf20Sopenharmony_ci vlan4k.untag, 3748c2ecf20Sopenharmony_ci 1); 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_vlan_filtering); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciint rtl8366_vlan_prepare(struct dsa_switch *ds, int port, 3838c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 3868c2ecf20Sopenharmony_ci u16 vid; 3878c2ecf20Sopenharmony_ci int ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) 3908c2ecf20Sopenharmony_ci if (!smi->ops->is_vlan_valid(smi, vid)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dev_info(smi->dev, "prepare VLANs %04x..%04x\n", 3948c2ecf20Sopenharmony_ci vlan->vid_begin, vlan->vid_end); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Enable VLAN in the hardware 3978c2ecf20Sopenharmony_ci * FIXME: what's with this 4k business? 3988c2ecf20Sopenharmony_ci * Just rtl8366_enable_vlan() seems inconclusive. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci ret = rtl8366_enable_vlan4k(smi, true); 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_vlan_prepare); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_civoid rtl8366_vlan_add(struct dsa_switch *ds, int port, 4098c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 4128c2ecf20Sopenharmony_ci bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); 4138c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 4148c2ecf20Sopenharmony_ci u32 member = 0; 4158c2ecf20Sopenharmony_ci u32 untag = 0; 4168c2ecf20Sopenharmony_ci u16 vid; 4178c2ecf20Sopenharmony_ci int ret; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++) 4208c2ecf20Sopenharmony_ci if (!smi->ops->is_vlan_valid(smi, vid)) 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n", 4248c2ecf20Sopenharmony_ci vlan->vid_begin, 4258c2ecf20Sopenharmony_ci port, 4268c2ecf20Sopenharmony_ci untagged ? "untagged" : "tagged", 4278c2ecf20Sopenharmony_ci pvid ? " PVID" : "no PVID"); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) 4308c2ecf20Sopenharmony_ci dev_err(smi->dev, "port is DSA or CPU port\n"); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 4338c2ecf20Sopenharmony_ci member |= BIT(port); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (untagged) 4368c2ecf20Sopenharmony_ci untag |= BIT(port); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = rtl8366_set_vlan(smi, vid, member, untag, 0); 4398c2ecf20Sopenharmony_ci if (ret) 4408c2ecf20Sopenharmony_ci dev_err(smi->dev, 4418c2ecf20Sopenharmony_ci "failed to set up VLAN %04x", 4428c2ecf20Sopenharmony_ci vid); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!pvid) 4458c2ecf20Sopenharmony_ci continue; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = rtl8366_set_pvid(smi, port, vid); 4488c2ecf20Sopenharmony_ci if (ret) 4498c2ecf20Sopenharmony_ci dev_err(smi->dev, 4508c2ecf20Sopenharmony_ci "failed to set PVID on port %d to VLAN %04x", 4518c2ecf20Sopenharmony_ci port, vid); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!ret) 4548c2ecf20Sopenharmony_ci dev_dbg(smi->dev, "VLAN add: added VLAN %d with PVID on port %d\n", 4558c2ecf20Sopenharmony_ci vid, port); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_vlan_add); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciint rtl8366_vlan_del(struct dsa_switch *ds, int port, 4618c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 4648c2ecf20Sopenharmony_ci u16 vid; 4658c2ecf20Sopenharmony_ci int ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci dev_info(smi->dev, "del VLAN on port %d\n", port); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { 4708c2ecf20Sopenharmony_ci int i; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci dev_info(smi->dev, "del VLAN %04x\n", vid); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_vlan_mc; i++) { 4758c2ecf20Sopenharmony_ci struct rtl8366_vlan_mc vlanmc; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); 4788c2ecf20Sopenharmony_ci if (ret) 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (vid == vlanmc.vid) { 4828c2ecf20Sopenharmony_ci /* Remove this port from the VLAN */ 4838c2ecf20Sopenharmony_ci vlanmc.member &= ~BIT(port); 4848c2ecf20Sopenharmony_ci vlanmc.untag &= ~BIT(port); 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * If no ports are members of this VLAN 4878c2ecf20Sopenharmony_ci * anymore then clear the whole member 4888c2ecf20Sopenharmony_ci * config so it can be reused. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci if (!vlanmc.member && vlanmc.untag) { 4918c2ecf20Sopenharmony_ci vlanmc.vid = 0; 4928c2ecf20Sopenharmony_ci vlanmc.priority = 0; 4938c2ecf20Sopenharmony_ci vlanmc.fid = 0; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); 4968c2ecf20Sopenharmony_ci if (ret) { 4978c2ecf20Sopenharmony_ci dev_err(smi->dev, 4988c2ecf20Sopenharmony_ci "failed to remove VLAN %04x\n", 4998c2ecf20Sopenharmony_ci vid); 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_vlan_del); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_civoid rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, 5128c2ecf20Sopenharmony_ci uint8_t *data) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 5158c2ecf20Sopenharmony_ci struct rtl8366_mib_counter *mib; 5168c2ecf20Sopenharmony_ci int i; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (port >= smi->num_ports) 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_mib_counters; i++) { 5228c2ecf20Sopenharmony_ci mib = &smi->mib_counters[i]; 5238c2ecf20Sopenharmony_ci strncpy(data + i * ETH_GSTRING_LEN, 5248c2ecf20Sopenharmony_ci mib->name, ETH_GSTRING_LEN); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_get_strings); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciint rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* We only support SS_STATS */ 5348c2ecf20Sopenharmony_ci if (sset != ETH_SS_STATS) 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci if (port >= smi->num_ports) 5378c2ecf20Sopenharmony_ci return -EINVAL; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return smi->num_mib_counters; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_get_sset_count); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_civoid rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct realtek_smi *smi = ds->priv; 5468c2ecf20Sopenharmony_ci int i; 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (port >= smi->num_ports) 5508c2ecf20Sopenharmony_ci return; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci for (i = 0; i < smi->num_mib_counters; i++) { 5538c2ecf20Sopenharmony_ci struct rtl8366_mib_counter *mib; 5548c2ecf20Sopenharmony_ci u64 mibvalue = 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci mib = &smi->mib_counters[i]; 5578c2ecf20Sopenharmony_ci ret = smi->ops->get_mib_counter(smi, port, mib, &mibvalue); 5588c2ecf20Sopenharmony_ci if (ret) { 5598c2ecf20Sopenharmony_ci dev_err(smi->dev, "error reading MIB counter %s\n", 5608c2ecf20Sopenharmony_ci mib->name); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci data[i] = mibvalue; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats); 566