18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright 2019 NXP Semiconductors 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This is an umbrella module for all network switches that are 58c2ecf20Sopenharmony_ci * register-compatible with Ocelot and that perform I/O to their host CPU 68c2ecf20Sopenharmony_ci * through an NPI (Node Processor Interface) Ethernet port. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <uapi/linux/if_bridge.h> 98c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_vcap.h> 108c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_qsys.h> 118c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_sys.h> 128c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_dev.h> 138c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_ana.h> 148c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot_ptp.h> 158c2ecf20Sopenharmony_ci#include <soc/mscc/ocelot.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/packing.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of_net.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/pcs-lynx.h> 238c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 248c2ecf20Sopenharmony_ci#include <net/dsa.h> 258c2ecf20Sopenharmony_ci#include "felix.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, 288c2ecf20Sopenharmony_ci int port, 298c2ecf20Sopenharmony_ci enum dsa_tag_protocol mp) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return DSA_TAG_PROTO_OCELOT; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int felix_set_ageing_time(struct dsa_switch *ds, 358c2ecf20Sopenharmony_ci unsigned int ageing_time) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci ocelot_set_ageing_time(ocelot, ageing_time); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int felix_fdb_dump(struct dsa_switch *ds, int port, 458c2ecf20Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return ocelot_fdb_dump(ocelot, port, cb, data); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int felix_fdb_add(struct dsa_switch *ds, int port, 538c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return ocelot_fdb_add(ocelot, port, addr, vid); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int felix_fdb_del(struct dsa_switch *ds, int port, 618c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return ocelot_fdb_del(ocelot, port, addr, vid); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* This callback needs to be present */ 698c2ecf20Sopenharmony_cistatic int felix_mdb_prepare(struct dsa_switch *ds, int port, 708c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void felix_mdb_add(struct dsa_switch *ds, int port, 768c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ocelot_port_mdb_add(ocelot, port, mdb); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int felix_mdb_del(struct dsa_switch *ds, int port, 848c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return ocelot_port_mdb_del(ocelot, port, mdb); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, 928c2ecf20Sopenharmony_ci u8 state) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return ocelot_bridge_stp_state_set(ocelot, port, state); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int felix_bridge_join(struct dsa_switch *ds, int port, 1008c2ecf20Sopenharmony_ci struct net_device *br) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return ocelot_port_bridge_join(ocelot, port, br); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void felix_bridge_leave(struct dsa_switch *ds, int port, 1088c2ecf20Sopenharmony_ci struct net_device *br) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ocelot_port_bridge_leave(ocelot, port, br); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* This callback needs to be present */ 1168c2ecf20Sopenharmony_cistatic int felix_vlan_prepare(struct dsa_switch *ds, int port, 1178c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, 1238c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return ocelot_port_vlan_filtering(ocelot, port, enabled, trans); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void felix_vlan_add(struct dsa_switch *ds, int port, 1318c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1348c2ecf20Sopenharmony_ci u16 flags = vlan->flags; 1358c2ecf20Sopenharmony_ci u16 vid; 1368c2ecf20Sopenharmony_ci int err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (dsa_is_cpu_port(ds, port)) 1398c2ecf20Sopenharmony_ci flags &= ~BRIDGE_VLAN_INFO_UNTAGGED; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 1428c2ecf20Sopenharmony_ci err = ocelot_vlan_add(ocelot, port, vid, 1438c2ecf20Sopenharmony_ci flags & BRIDGE_VLAN_INFO_PVID, 1448c2ecf20Sopenharmony_ci flags & BRIDGE_VLAN_INFO_UNTAGGED); 1458c2ecf20Sopenharmony_ci if (err) { 1468c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n", 1478c2ecf20Sopenharmony_ci vid, port, err); 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int felix_vlan_del(struct dsa_switch *ds, int port, 1548c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1578c2ecf20Sopenharmony_ci u16 vid; 1588c2ecf20Sopenharmony_ci int err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 1618c2ecf20Sopenharmony_ci err = ocelot_vlan_del(ocelot, port, vid); 1628c2ecf20Sopenharmony_ci if (err) { 1638c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", 1648c2ecf20Sopenharmony_ci vid, port, err); 1658c2ecf20Sopenharmony_ci return err; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int felix_port_enable(struct dsa_switch *ds, int port, 1728c2ecf20Sopenharmony_ci struct phy_device *phy) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ocelot_port_enable(ocelot, port, phy); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void felix_port_disable(struct dsa_switch *ds, int port) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ocelot_port_disable(ocelot, port); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void felix_phylink_validate(struct dsa_switch *ds, int port, 1898c2ecf20Sopenharmony_ci unsigned long *supported, 1908c2ecf20Sopenharmony_ci struct phylink_link_state *state) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 1938c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (felix->info->phylink_validate) 1968c2ecf20Sopenharmony_ci felix->info->phylink_validate(ocelot, port, supported, state); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void felix_phylink_mac_config(struct dsa_switch *ds, int port, 2008c2ecf20Sopenharmony_ci unsigned int link_an_mode, 2018c2ecf20Sopenharmony_ci const struct phylink_link_state *state) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 2048c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 2058c2ecf20Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (felix->pcs[port]) 2088c2ecf20Sopenharmony_ci phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, 2128c2ecf20Sopenharmony_ci unsigned int link_an_mode, 2138c2ecf20Sopenharmony_ci phy_interface_t interface) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 2168c2ecf20Sopenharmony_ci struct ocelot_port *ocelot_port = ocelot->ports[port]; 2178c2ecf20Sopenharmony_ci int err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ocelot_port_rmwl(ocelot_port, 0, DEV_MAC_ENA_CFG_RX_ENA, 2208c2ecf20Sopenharmony_ci DEV_MAC_ENA_CFG); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci err = ocelot_port_flush(ocelot, port); 2258c2ecf20Sopenharmony_ci if (err) 2268c2ecf20Sopenharmony_ci dev_err(ocelot->dev, "failed to flush port %d: %d\n", 2278c2ecf20Sopenharmony_ci port, err); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Put the port in reset. */ 2308c2ecf20Sopenharmony_ci ocelot_port_writel(ocelot_port, 2318c2ecf20Sopenharmony_ci DEV_CLOCK_CFG_MAC_TX_RST | 2328c2ecf20Sopenharmony_ci DEV_CLOCK_CFG_MAC_RX_RST | 2338c2ecf20Sopenharmony_ci DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000), 2348c2ecf20Sopenharmony_ci DEV_CLOCK_CFG); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, 2388c2ecf20Sopenharmony_ci unsigned int link_an_mode, 2398c2ecf20Sopenharmony_ci phy_interface_t interface, 2408c2ecf20Sopenharmony_ci struct phy_device *phydev, 2418c2ecf20Sopenharmony_ci int speed, int duplex, 2428c2ecf20Sopenharmony_ci bool tx_pause, bool rx_pause) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 2458c2ecf20Sopenharmony_ci struct ocelot_port *ocelot_port = ocelot->ports[port]; 2468c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 2478c2ecf20Sopenharmony_ci u32 mac_fc_cfg; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and 2508c2ecf20Sopenharmony_ci * PORT_RST bits in DEV_CLOCK_CFG. Note that the way this system is 2518c2ecf20Sopenharmony_ci * integrated is that the MAC speed is fixed and it's the PCS who is 2528c2ecf20Sopenharmony_ci * performing the rate adaptation, so we have to write "1000Mbps" into 2538c2ecf20Sopenharmony_ci * the LINK_SPEED field of DEV_CLOCK_CFG (which is also its default 2548c2ecf20Sopenharmony_ci * value). 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci ocelot_port_writel(ocelot_port, 2578c2ecf20Sopenharmony_ci DEV_CLOCK_CFG_LINK_SPEED(OCELOT_SPEED_1000), 2588c2ecf20Sopenharmony_ci DEV_CLOCK_CFG); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci switch (speed) { 2618c2ecf20Sopenharmony_ci case SPEED_10: 2628c2ecf20Sopenharmony_ci mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(3); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case SPEED_100: 2658c2ecf20Sopenharmony_ci mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(2); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case SPEED_1000: 2688c2ecf20Sopenharmony_ci case SPEED_2500: 2698c2ecf20Sopenharmony_ci mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(1); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci default: 2728c2ecf20Sopenharmony_ci dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n", 2738c2ecf20Sopenharmony_ci port, speed); 2748c2ecf20Sopenharmony_ci return; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* handle Rx pause in all cases, with 2500base-X this is used for rate 2788c2ecf20Sopenharmony_ci * adaptation. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (tx_pause) 2838c2ecf20Sopenharmony_ci mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | 2848c2ecf20Sopenharmony_ci SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | 2858c2ecf20Sopenharmony_ci SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | 2868c2ecf20Sopenharmony_ci SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Flow control. Link speed is only used here to evaluate the time 2898c2ecf20Sopenharmony_ci * specification in incoming pause frames. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Undo the effects of felix_phylink_mac_link_down: 2968c2ecf20Sopenharmony_ci * enable MAC module 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | 2998c2ecf20Sopenharmony_ci DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Enable receiving frames on the port, and activate auto-learning of 3028c2ecf20Sopenharmony_ci * MAC addresses. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO | 3058c2ecf20Sopenharmony_ci ANA_PORT_PORT_CFG_RECV_ENA | 3068c2ecf20Sopenharmony_ci ANA_PORT_PORT_CFG_PORTID_VAL(port), 3078c2ecf20Sopenharmony_ci ANA_PORT_PORT_CFG, port); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Core: Enable port for frame transfer */ 3108c2ecf20Sopenharmony_ci ocelot_fields_write(ocelot, port, 3118c2ecf20Sopenharmony_ci QSYS_SWITCH_PORT_MODE_PORT_ENA, 1); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (felix->info->port_sched_speed_set) 3148c2ecf20Sopenharmony_ci felix->info->port_sched_speed_set(ocelot, port, speed); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void felix_port_qos_map_init(struct ocelot *ocelot, int port) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci int i; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ocelot_rmw_gix(ocelot, 3228c2ecf20Sopenharmony_ci ANA_PORT_QOS_CFG_QOS_PCP_ENA, 3238c2ecf20Sopenharmony_ci ANA_PORT_QOS_CFG_QOS_PCP_ENA, 3248c2ecf20Sopenharmony_ci ANA_PORT_QOS_CFG, 3258c2ecf20Sopenharmony_ci port); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < FELIX_NUM_TC * 2; i++) { 3288c2ecf20Sopenharmony_ci ocelot_rmw_ix(ocelot, 3298c2ecf20Sopenharmony_ci (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) | 3308c2ecf20Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i), 3318c2ecf20Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL | 3328c2ecf20Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL_M, 3338c2ecf20Sopenharmony_ci ANA_PORT_PCP_DEI_MAP, 3348c2ecf20Sopenharmony_ci port, i); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void felix_get_strings(struct dsa_switch *ds, int port, 3398c2ecf20Sopenharmony_ci u32 stringset, u8 *data) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return ocelot_get_strings(ocelot, port, stringset, data); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void felix_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ocelot_get_ethtool_stats(ocelot, port, data); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int felix_get_sset_count(struct dsa_switch *ds, int port, int sset) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return ocelot_get_sset_count(ocelot, port, sset); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int felix_get_ts_info(struct dsa_switch *ds, int port, 3618c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return ocelot_get_ts_info(ocelot, port, info); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int felix_parse_ports_node(struct felix *felix, 3698c2ecf20Sopenharmony_ci struct device_node *ports_node, 3708c2ecf20Sopenharmony_ci phy_interface_t *port_phy_modes) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct ocelot *ocelot = &felix->ocelot; 3738c2ecf20Sopenharmony_ci struct device *dev = felix->ocelot.dev; 3748c2ecf20Sopenharmony_ci struct device_node *child; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for_each_available_child_of_node(ports_node, child) { 3778c2ecf20Sopenharmony_ci phy_interface_t phy_mode; 3788c2ecf20Sopenharmony_ci u32 port; 3798c2ecf20Sopenharmony_ci int err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Get switch port number from DT */ 3828c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &port) < 0) { 3838c2ecf20Sopenharmony_ci dev_err(dev, "Port number not defined in device tree " 3848c2ecf20Sopenharmony_ci "(property \"reg\")\n"); 3858c2ecf20Sopenharmony_ci of_node_put(child); 3868c2ecf20Sopenharmony_ci return -ENODEV; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Get PHY mode from DT */ 3908c2ecf20Sopenharmony_ci err = of_get_phy_mode(child, &phy_mode); 3918c2ecf20Sopenharmony_ci if (err) { 3928c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read phy-mode or " 3938c2ecf20Sopenharmony_ci "phy-interface-type property for port %d\n", 3948c2ecf20Sopenharmony_ci port); 3958c2ecf20Sopenharmony_ci of_node_put(child); 3968c2ecf20Sopenharmony_ci return -ENODEV; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci err = felix->info->prevalidate_phy_mode(ocelot, port, phy_mode); 4008c2ecf20Sopenharmony_ci if (err < 0) { 4018c2ecf20Sopenharmony_ci dev_err(dev, "Unsupported PHY mode %s on port %d\n", 4028c2ecf20Sopenharmony_ci phy_modes(phy_mode), port); 4038c2ecf20Sopenharmony_ci of_node_put(child); 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci port_phy_modes[port] = phy_mode; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int felix_parse_dt(struct felix *felix, phy_interface_t *port_phy_modes) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct device *dev = felix->ocelot.dev; 4168c2ecf20Sopenharmony_ci struct device_node *switch_node; 4178c2ecf20Sopenharmony_ci struct device_node *ports_node; 4188c2ecf20Sopenharmony_ci int err; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci switch_node = dev->of_node; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ports"); 4238c2ecf20Sopenharmony_ci if (!ports_node) { 4248c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); 4258c2ecf20Sopenharmony_ci return -ENODEV; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci err = felix_parse_ports_node(felix, ports_node, port_phy_modes); 4298c2ecf20Sopenharmony_ci of_node_put(ports_node); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return err; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int felix_init_structs(struct felix *felix, int num_phys_ports) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct ocelot *ocelot = &felix->ocelot; 4378c2ecf20Sopenharmony_ci phy_interface_t *port_phy_modes; 4388c2ecf20Sopenharmony_ci struct resource res; 4398c2ecf20Sopenharmony_ci int port, i, err; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ocelot->num_phys_ports = num_phys_ports; 4428c2ecf20Sopenharmony_ci ocelot->ports = devm_kcalloc(ocelot->dev, num_phys_ports, 4438c2ecf20Sopenharmony_ci sizeof(struct ocelot_port *), GFP_KERNEL); 4448c2ecf20Sopenharmony_ci if (!ocelot->ports) 4458c2ecf20Sopenharmony_ci return -ENOMEM; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ocelot->map = felix->info->map; 4488c2ecf20Sopenharmony_ci ocelot->stats_layout = felix->info->stats_layout; 4498c2ecf20Sopenharmony_ci ocelot->num_stats = felix->info->num_stats; 4508c2ecf20Sopenharmony_ci ocelot->shared_queue_sz = felix->info->shared_queue_sz; 4518c2ecf20Sopenharmony_ci ocelot->num_mact_rows = felix->info->num_mact_rows; 4528c2ecf20Sopenharmony_ci ocelot->vcap = felix->info->vcap; 4538c2ecf20Sopenharmony_ci ocelot->ops = felix->info->ops; 4548c2ecf20Sopenharmony_ci ocelot->inj_prefix = OCELOT_TAG_PREFIX_SHORT; 4558c2ecf20Sopenharmony_ci ocelot->xtr_prefix = OCELOT_TAG_PREFIX_SHORT; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), 4588c2ecf20Sopenharmony_ci GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (!port_phy_modes) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = felix_parse_dt(felix, port_phy_modes); 4638c2ecf20Sopenharmony_ci if (err) { 4648c2ecf20Sopenharmony_ci kfree(port_phy_modes); 4658c2ecf20Sopenharmony_ci return err; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci for (i = 0; i < TARGET_MAX; i++) { 4698c2ecf20Sopenharmony_ci struct regmap *target; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (!felix->info->target_io_res[i].name) 4728c2ecf20Sopenharmony_ci continue; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci memcpy(&res, &felix->info->target_io_res[i], sizeof(res)); 4758c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 4768c2ecf20Sopenharmony_ci res.start += felix->switch_base; 4778c2ecf20Sopenharmony_ci res.end += felix->switch_base; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci target = ocelot_regmap_init(ocelot, &res); 4808c2ecf20Sopenharmony_ci if (IS_ERR(target)) { 4818c2ecf20Sopenharmony_ci dev_err(ocelot->dev, 4828c2ecf20Sopenharmony_ci "Failed to map device memory space\n"); 4838c2ecf20Sopenharmony_ci kfree(port_phy_modes); 4848c2ecf20Sopenharmony_ci return PTR_ERR(target); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ocelot->targets[i] = target; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci err = ocelot_regfields_init(ocelot, felix->info->regfields); 4918c2ecf20Sopenharmony_ci if (err) { 4928c2ecf20Sopenharmony_ci dev_err(ocelot->dev, "failed to init reg fields map\n"); 4938c2ecf20Sopenharmony_ci kfree(port_phy_modes); 4948c2ecf20Sopenharmony_ci return err; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (port = 0; port < num_phys_ports; port++) { 4988c2ecf20Sopenharmony_ci struct ocelot_port *ocelot_port; 4998c2ecf20Sopenharmony_ci struct regmap *target; 5008c2ecf20Sopenharmony_ci u8 *template; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ocelot_port = devm_kzalloc(ocelot->dev, 5038c2ecf20Sopenharmony_ci sizeof(struct ocelot_port), 5048c2ecf20Sopenharmony_ci GFP_KERNEL); 5058c2ecf20Sopenharmony_ci if (!ocelot_port) { 5068c2ecf20Sopenharmony_ci dev_err(ocelot->dev, 5078c2ecf20Sopenharmony_ci "failed to allocate port memory\n"); 5088c2ecf20Sopenharmony_ci kfree(port_phy_modes); 5098c2ecf20Sopenharmony_ci return -ENOMEM; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci memcpy(&res, &felix->info->port_io_res[port], sizeof(res)); 5138c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 5148c2ecf20Sopenharmony_ci res.start += felix->switch_base; 5158c2ecf20Sopenharmony_ci res.end += felix->switch_base; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci target = ocelot_regmap_init(ocelot, &res); 5188c2ecf20Sopenharmony_ci if (IS_ERR(target)) { 5198c2ecf20Sopenharmony_ci dev_err(ocelot->dev, 5208c2ecf20Sopenharmony_ci "Failed to map memory space for port %d\n", 5218c2ecf20Sopenharmony_ci port); 5228c2ecf20Sopenharmony_ci kfree(port_phy_modes); 5238c2ecf20Sopenharmony_ci return PTR_ERR(target); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci template = devm_kzalloc(ocelot->dev, OCELOT_TOTAL_TAG_LEN, 5278c2ecf20Sopenharmony_ci GFP_KERNEL); 5288c2ecf20Sopenharmony_ci if (!template) { 5298c2ecf20Sopenharmony_ci dev_err(ocelot->dev, 5308c2ecf20Sopenharmony_ci "Failed to allocate memory for DSA tag\n"); 5318c2ecf20Sopenharmony_ci kfree(port_phy_modes); 5328c2ecf20Sopenharmony_ci return -ENOMEM; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ocelot_port->phy_mode = port_phy_modes[port]; 5368c2ecf20Sopenharmony_ci ocelot_port->ocelot = ocelot; 5378c2ecf20Sopenharmony_ci ocelot_port->target = target; 5388c2ecf20Sopenharmony_ci ocelot_port->xmit_template = template; 5398c2ecf20Sopenharmony_ci ocelot->ports[port] = ocelot_port; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci felix->info->xmit_template_populate(ocelot, port); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci kfree(port_phy_modes); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (felix->info->mdio_bus_alloc) { 5478c2ecf20Sopenharmony_ci err = felix->info->mdio_bus_alloc(ocelot); 5488c2ecf20Sopenharmony_ci if (err < 0) 5498c2ecf20Sopenharmony_ci return err; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/* The CPU port module is connected to the Node Processor Interface (NPI). This 5568c2ecf20Sopenharmony_ci * is the mode through which frames can be injected from and extracted to an 5578c2ecf20Sopenharmony_ci * external CPU, over Ethernet. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_cistatic void felix_npi_port_init(struct ocelot *ocelot, int port) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci ocelot->npi = port; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | 5648c2ecf20Sopenharmony_ci QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port), 5658c2ecf20Sopenharmony_ci QSYS_EXT_CPU_CFG); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* NPI port Injection/Extraction configuration */ 5688c2ecf20Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, 5698c2ecf20Sopenharmony_ci ocelot->xtr_prefix); 5708c2ecf20Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, 5718c2ecf20Sopenharmony_ci ocelot->inj_prefix); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* Disable transmission of pause frames */ 5748c2ecf20Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* Hardware initialization done here so that we can allocate structures with 5788c2ecf20Sopenharmony_ci * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing 5798c2ecf20Sopenharmony_ci * us to allocate structures twice (leak memory) and map PCI memory twice 5808c2ecf20Sopenharmony_ci * (which will not work). 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistatic int felix_setup(struct dsa_switch *ds) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 5858c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 5868c2ecf20Sopenharmony_ci int port, err; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci err = felix_init_structs(felix, ds->num_ports); 5898c2ecf20Sopenharmony_ci if (err) 5908c2ecf20Sopenharmony_ci return err; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci err = ocelot_init(ocelot); 5938c2ecf20Sopenharmony_ci if (err) 5948c2ecf20Sopenharmony_ci return err; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (ocelot->ptp) { 5978c2ecf20Sopenharmony_ci err = ocelot_init_timestamp(ocelot, felix->info->ptp_caps); 5988c2ecf20Sopenharmony_ci if (err) { 5998c2ecf20Sopenharmony_ci dev_err(ocelot->dev, 6008c2ecf20Sopenharmony_ci "Timestamp initialization failed\n"); 6018c2ecf20Sopenharmony_ci ocelot->ptp = 0; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 6068c2ecf20Sopenharmony_ci ocelot_init_port(ocelot, port); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (dsa_is_cpu_port(ds, port)) 6098c2ecf20Sopenharmony_ci felix_npi_port_init(ocelot, port); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Set the default QoS Classification based on PCP and DEI 6128c2ecf20Sopenharmony_ci * bits of vlan tag. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_ci felix_port_qos_map_init(ocelot, port); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* Include the CPU port module in the forwarding mask for unknown 6188c2ecf20Sopenharmony_ci * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST 6198c2ecf20Sopenharmony_ci * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since 6208c2ecf20Sopenharmony_ci * Ocelot relies on whitelisting MAC addresses towards PGID_CPU. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci ocelot_write_rix(ocelot, 6238c2ecf20Sopenharmony_ci ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)), 6248c2ecf20Sopenharmony_ci ANA_PGID_PGID, PGID_UC); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci ds->mtu_enforcement_ingress = true; 6278c2ecf20Sopenharmony_ci ds->configure_vlan_while_not_filtering = true; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void felix_teardown(struct dsa_switch *ds) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 6358c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 6368c2ecf20Sopenharmony_ci int port; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ocelot_deinit_timestamp(ocelot); 6398c2ecf20Sopenharmony_ci ocelot_deinit(ocelot); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (port = 0; port < ocelot->num_phys_ports; port++) { 6428c2ecf20Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 6438c2ecf20Sopenharmony_ci continue; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci ocelot_deinit_port(ocelot, port); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (felix->info->mdio_bus_free) 6498c2ecf20Sopenharmony_ci felix->info->mdio_bus_free(ocelot); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int felix_hwtstamp_get(struct dsa_switch *ds, int port, 6538c2ecf20Sopenharmony_ci struct ifreq *ifr) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return ocelot_hwstamp_get(ocelot, port, ifr); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int felix_hwtstamp_set(struct dsa_switch *ds, int port, 6618c2ecf20Sopenharmony_ci struct ifreq *ifr) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return ocelot_hwstamp_set(ocelot, port, ifr); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic bool felix_rxtstamp(struct dsa_switch *ds, int port, 6698c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned int type) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 6728c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 6738c2ecf20Sopenharmony_ci u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN; 6748c2ecf20Sopenharmony_ci u32 tstamp_lo, tstamp_hi; 6758c2ecf20Sopenharmony_ci struct timespec64 ts; 6768c2ecf20Sopenharmony_ci u64 tstamp, val; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); 6798c2ecf20Sopenharmony_ci tstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci packing(extraction, &val, 116, 85, OCELOT_TAG_LEN, UNPACK, 0); 6828c2ecf20Sopenharmony_ci tstamp_lo = (u32)val; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci tstamp_hi = tstamp >> 32; 6858c2ecf20Sopenharmony_ci if ((tstamp & 0xffffffff) < tstamp_lo) 6868c2ecf20Sopenharmony_ci tstamp_hi--; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci tstamp = ((u64)tstamp_hi << 32) | tstamp_lo; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 6918c2ecf20Sopenharmony_ci memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); 6928c2ecf20Sopenharmony_ci shhwtstamps->hwtstamp = tstamp; 6938c2ecf20Sopenharmony_ci return false; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic bool felix_txtstamp(struct dsa_switch *ds, int port, 6978c2ecf20Sopenharmony_ci struct sk_buff *clone, unsigned int type) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7008c2ecf20Sopenharmony_ci struct ocelot_port *ocelot_port = ocelot->ports[port]; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (ocelot->ptp && (skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP) && 7038c2ecf20Sopenharmony_ci ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { 7048c2ecf20Sopenharmony_ci ocelot_port_add_txtstamp_skb(ocelot, port, clone); 7058c2ecf20Sopenharmony_ci return true; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return false; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ocelot_port_set_maxlen(ocelot, port, new_mtu); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return 0; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int felix_get_max_mtu(struct dsa_switch *ds, int port) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return ocelot_get_max_mtu(ocelot, port); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int felix_cls_flower_add(struct dsa_switch *ds, int port, 7288c2ecf20Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return ocelot_cls_flower_replace(ocelot, port, cls, ingress); 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic int felix_cls_flower_del(struct dsa_switch *ds, int port, 7368c2ecf20Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return ocelot_cls_flower_destroy(ocelot, port, cls, ingress); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic int felix_cls_flower_stats(struct dsa_switch *ds, int port, 7448c2ecf20Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return ocelot_cls_flower_stats(ocelot, port, cls, ingress); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int felix_port_policer_add(struct dsa_switch *ds, int port, 7528c2ecf20Sopenharmony_ci struct dsa_mall_policer_tc_entry *policer) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7558c2ecf20Sopenharmony_ci struct ocelot_policer pol = { 7568c2ecf20Sopenharmony_ci .rate = div_u64(policer->rate_bytes_per_sec, 1000) * 8, 7578c2ecf20Sopenharmony_ci .burst = policer->burst, 7588c2ecf20Sopenharmony_ci }; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci return ocelot_port_policer_add(ocelot, port, &pol); 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic void felix_port_policer_del(struct dsa_switch *ds, int port) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ocelot_port_policer_del(ocelot, port); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int felix_port_setup_tc(struct dsa_switch *ds, int port, 7718c2ecf20Sopenharmony_ci enum tc_setup_type type, 7728c2ecf20Sopenharmony_ci void *type_data) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7758c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (felix->info->port_setup_tc) 7788c2ecf20Sopenharmony_ci return felix->info->port_setup_tc(ds, port, type, type_data); 7798c2ecf20Sopenharmony_ci else 7808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ciconst struct dsa_switch_ops felix_switch_ops = { 7848c2ecf20Sopenharmony_ci .get_tag_protocol = felix_get_tag_protocol, 7858c2ecf20Sopenharmony_ci .setup = felix_setup, 7868c2ecf20Sopenharmony_ci .teardown = felix_teardown, 7878c2ecf20Sopenharmony_ci .set_ageing_time = felix_set_ageing_time, 7888c2ecf20Sopenharmony_ci .get_strings = felix_get_strings, 7898c2ecf20Sopenharmony_ci .get_ethtool_stats = felix_get_ethtool_stats, 7908c2ecf20Sopenharmony_ci .get_sset_count = felix_get_sset_count, 7918c2ecf20Sopenharmony_ci .get_ts_info = felix_get_ts_info, 7928c2ecf20Sopenharmony_ci .phylink_validate = felix_phylink_validate, 7938c2ecf20Sopenharmony_ci .phylink_mac_config = felix_phylink_mac_config, 7948c2ecf20Sopenharmony_ci .phylink_mac_link_down = felix_phylink_mac_link_down, 7958c2ecf20Sopenharmony_ci .phylink_mac_link_up = felix_phylink_mac_link_up, 7968c2ecf20Sopenharmony_ci .port_enable = felix_port_enable, 7978c2ecf20Sopenharmony_ci .port_disable = felix_port_disable, 7988c2ecf20Sopenharmony_ci .port_fdb_dump = felix_fdb_dump, 7998c2ecf20Sopenharmony_ci .port_fdb_add = felix_fdb_add, 8008c2ecf20Sopenharmony_ci .port_fdb_del = felix_fdb_del, 8018c2ecf20Sopenharmony_ci .port_mdb_prepare = felix_mdb_prepare, 8028c2ecf20Sopenharmony_ci .port_mdb_add = felix_mdb_add, 8038c2ecf20Sopenharmony_ci .port_mdb_del = felix_mdb_del, 8048c2ecf20Sopenharmony_ci .port_bridge_join = felix_bridge_join, 8058c2ecf20Sopenharmony_ci .port_bridge_leave = felix_bridge_leave, 8068c2ecf20Sopenharmony_ci .port_stp_state_set = felix_bridge_stp_state_set, 8078c2ecf20Sopenharmony_ci .port_vlan_prepare = felix_vlan_prepare, 8088c2ecf20Sopenharmony_ci .port_vlan_filtering = felix_vlan_filtering, 8098c2ecf20Sopenharmony_ci .port_vlan_add = felix_vlan_add, 8108c2ecf20Sopenharmony_ci .port_vlan_del = felix_vlan_del, 8118c2ecf20Sopenharmony_ci .port_hwtstamp_get = felix_hwtstamp_get, 8128c2ecf20Sopenharmony_ci .port_hwtstamp_set = felix_hwtstamp_set, 8138c2ecf20Sopenharmony_ci .port_rxtstamp = felix_rxtstamp, 8148c2ecf20Sopenharmony_ci .port_txtstamp = felix_txtstamp, 8158c2ecf20Sopenharmony_ci .port_change_mtu = felix_change_mtu, 8168c2ecf20Sopenharmony_ci .port_max_mtu = felix_get_max_mtu, 8178c2ecf20Sopenharmony_ci .port_policer_add = felix_port_policer_add, 8188c2ecf20Sopenharmony_ci .port_policer_del = felix_port_policer_del, 8198c2ecf20Sopenharmony_ci .cls_flower_add = felix_cls_flower_add, 8208c2ecf20Sopenharmony_ci .cls_flower_del = felix_cls_flower_del, 8218c2ecf20Sopenharmony_ci .cls_flower_stats = felix_cls_flower_stats, 8228c2ecf20Sopenharmony_ci .port_setup_tc = felix_port_setup_tc, 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistruct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 8288c2ecf20Sopenharmony_ci struct dsa_switch *ds = felix->ds; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 8318c2ecf20Sopenharmony_ci return NULL; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci return dsa_to_port(ds, port)->slave; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ciint felix_netdev_to_port(struct net_device *dev) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct dsa_port *dp; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci dp = dsa_port_from_netdev(dev); 8418c2ecf20Sopenharmony_ci if (IS_ERR(dp)) 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return dp->index; 8458c2ecf20Sopenharmony_ci} 846