Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-renesas-rswitch-r-car-s4-add-hw-offloading-for-layer-2-switching'

Michael Dege says:

====================
net: renesas: rswitch: R-Car S4 add HW offloading for layer 2 switching

The current R-Car S4 rswitch driver only supports port based fowarding.
This patch set adds HW offloading for L2 switching/bridgeing. The driver
hooks into switchdev.

1. Rename the base driver file to keep the driver name (rswitch.ko)

2. Add setting of default MAC ageing time in hardware.

3. Add the L2 driver extension in a separate file. The HW offloading
is automatically configured when a port is added to the bridge device.

Usage example:
ip link add name br0 type bridge
ip link set dev tsn0 master br0
ip link set dev tsn1 master br0
ip link set dev br0 up
ip link set dev tsn0 up
ip link set dev tsn1 up

Layer 2 traffic is now fowarded by HW from port TSN0 to port TSN1.

4. Provides the functionality to set the MAC table ageing time in the
Rswitch.

Usage example:
ip link change dev br0 type bridge ageing 100

v4: https://lore.kernel.org/r/20250828-add_l2_switching-v4-0-89d7108c8592@renesas.com
v3: https://lore.kernel.org/r/20250710-add_l2_switching-v3-0-c0a328327b43@renesas.com
v2: https://lore.kernel.org/r/20250708-add_l2_switching-v2-0-f91f5556617a@renesas.com
v1: https://lore.kernel.org/r/20250704-add_l2_switching-v1-0-ff882aacb258@renesas.com

Signed-off-by: Michael Dege <michael.dege@renesas.com>
Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
====================

Link: https://patch.msgid.link/20250901-add_l2_switching-v5-0-5f13e46860d5@renesas.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+455 -6
+1
drivers/net/ethernet/renesas/Makefile
··· 8 8 ravb-objs := ravb_main.o ravb_ptp.o 9 9 obj-$(CONFIG_RAVB) += ravb.o 10 10 11 + rswitch-objs := rswitch_main.o rswitch_l2.o 11 12 obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch.o 12 13 13 14 obj-$(CONFIG_RENESAS_GEN4_PTP) += rcar_gen4_ptp.o
+82 -4
drivers/net/ethernet/renesas/rswitch.c drivers/net/ethernet/renesas/rswitch_main.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* Renesas Ethernet Switch device driver 3 3 * 4 - * Copyright (C) 2022 Renesas Electronics Corporation 4 + * Copyright (C) 2022-2025 Renesas Electronics Corporation 5 5 */ 6 6 7 7 #include <linux/clk.h> 8 8 #include <linux/dma-mapping.h> 9 9 #include <linux/err.h> 10 10 #include <linux/etherdevice.h> 11 + #include <linux/ethtool.h> 12 + #include <linux/ip.h> 11 13 #include <linux/iopoll.h> 12 14 #include <linux/kernel.h> 15 + #include <linux/list.h> 13 16 #include <linux/module.h> 14 17 #include <linux/net_tstamp.h> 15 18 #include <linux/of.h> ··· 28 25 #include <linux/sys_soc.h> 29 26 30 27 #include "rswitch.h" 28 + #include "rswitch_l2.h" 31 29 32 30 static int rswitch_reg_wait(void __iomem *addr, u32 offs, u32 mask, u32 expected) 33 31 { ··· 38 34 1, RSWITCH_TIMEOUT_US); 39 35 } 40 36 41 - static void rswitch_modify(void __iomem *addr, enum rswitch_reg reg, u32 clear, u32 set) 37 + void rswitch_modify(void __iomem *addr, enum rswitch_reg reg, u32 clear, u32 set) 42 38 { 43 39 iowrite32((ioread32(addr + reg) & ~clear) | set, addr + reg); 44 40 } ··· 113 109 } 114 110 115 111 /* Forwarding engine block (MFWD) */ 116 - static void rswitch_fwd_init(struct rswitch_private *priv) 112 + static int rswitch_fwd_init(struct rswitch_private *priv) 117 113 { 118 114 u32 all_ports_mask = GENMASK(RSWITCH_NUM_AGENTS - 1, 0); 119 115 unsigned int i; 116 + u32 reg_val; 120 117 121 118 /* Start with empty configuration */ 122 119 for (i = 0; i < RSWITCH_NUM_AGENTS; i++) { ··· 133 128 iowrite32(0, priv->addr + FWPBFC(i)); 134 129 } 135 130 131 + /* Configure MAC table aging */ 132 + rswitch_modify(priv->addr, FWMACAGUSPC, FWMACAGUSPC_MACAGUSP, 133 + FIELD_PREP(FWMACAGUSPC_MACAGUSP, RSW_AGEING_CLK_PER_US)); 134 + 135 + reg_val = FIELD_PREP(FWMACAGC_MACAGT, RSW_AGEING_TIME); 136 + reg_val |= FWMACAGC_MACAGE | FWMACAGC_MACAGSL; 137 + iowrite32(reg_val, priv->addr + FWMACAGC); 138 + 136 139 /* For enabled ETHA ports, setup port based forwarding */ 137 140 rswitch_for_each_enabled_port(priv, i) { 138 141 /* Port based forwarding from port i to GWCA port */ ··· 153 140 154 141 /* For GWCA port, allow direct descriptor forwarding */ 155 142 rswitch_modify(priv->addr, FWPC1(priv->gwca.index), FWPC1_DDE, FWPC1_DDE); 143 + 144 + /* Initialize hardware L2 forwarding table */ 145 + 146 + /* Allow entire table to be used for "unsecure" entries */ 147 + rswitch_modify(priv->addr, FWMACHEC, 0, FWMACHEC_MACHMUE_MASK); 148 + 149 + /* Initialize MAC hash table */ 150 + iowrite32(FWMACTIM_MACTIOG, priv->addr + FWMACTIM); 151 + 152 + return rswitch_reg_wait(priv->addr, FWMACTIM, FWMACTIM_MACTIOG, 0); 156 153 } 157 154 158 155 /* Gateway CPU agent block (GWCA) */ ··· 1625 1602 1626 1603 netif_start_queue(ndev); 1627 1604 1605 + if (rdev->brdev) 1606 + rswitch_update_l2_offload(rdev->priv); 1607 + 1628 1608 return 0; 1629 1609 }; 1630 1610 ··· 1649 1623 spin_unlock_irqrestore(&rdev->priv->lock, flags); 1650 1624 1651 1625 napi_disable(&rdev->napi); 1626 + 1627 + if (rdev->brdev) 1628 + rswitch_update_l2_offload(rdev->priv); 1652 1629 1653 1630 if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS)) 1654 1631 iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDID); ··· 1879 1850 } 1880 1851 } 1881 1852 1853 + static int rswitch_get_port_parent_id(struct net_device *ndev, 1854 + struct netdev_phys_item_id *ppid) 1855 + { 1856 + struct rswitch_device *rdev = netdev_priv(ndev); 1857 + const char *name; 1858 + 1859 + name = dev_name(&rdev->priv->pdev->dev); 1860 + ppid->id_len = min_t(size_t, strlen(name), sizeof(ppid->id)); 1861 + memcpy(ppid->id, name, ppid->id_len); 1862 + 1863 + return 0; 1864 + } 1865 + 1866 + static int rswitch_get_phys_port_name(struct net_device *ndev, 1867 + char *name, size_t len) 1868 + { 1869 + struct rswitch_device *rdev = netdev_priv(ndev); 1870 + 1871 + snprintf(name, len, "tsn%d", rdev->port); 1872 + 1873 + return 0; 1874 + } 1875 + 1882 1876 static const struct net_device_ops rswitch_netdev_ops = { 1883 1877 .ndo_open = rswitch_open, 1884 1878 .ndo_stop = rswitch_stop, 1885 1879 .ndo_start_xmit = rswitch_start_xmit, 1886 1880 .ndo_get_stats = rswitch_get_stats, 1887 1881 .ndo_eth_ioctl = rswitch_eth_ioctl, 1882 + .ndo_get_port_parent_id = rswitch_get_port_parent_id, 1883 + .ndo_get_phys_port_name = rswitch_get_phys_port_name, 1888 1884 .ndo_validate_addr = eth_validate_addr, 1889 1885 .ndo_set_mac_address = eth_mac_addr, 1890 1886 }; 1887 + 1888 + bool is_rdev(const struct net_device *ndev) 1889 + { 1890 + return (ndev->netdev_ops == &rswitch_netdev_ops); 1891 + } 1891 1892 1892 1893 static int rswitch_get_ts_info(struct net_device *ndev, struct kernel_ethtool_ts_info *info) 1893 1894 { ··· 2018 1959 if (err < 0) 2019 1960 goto out_txdmac; 2020 1961 1962 + list_add_tail(&rdev->list, &priv->port_list); 1963 + 2021 1964 return 0; 2022 1965 2023 1966 out_txdmac: ··· 2039 1978 struct rswitch_device *rdev = priv->rdev[index]; 2040 1979 struct net_device *ndev = rdev->ndev; 2041 1980 1981 + list_del(&rdev->list); 2042 1982 rswitch_txdmac_free(ndev); 2043 1983 rswitch_rxdmac_free(ndev); 2044 1984 of_node_put(rdev->np_port); ··· 2086 2024 } 2087 2025 } 2088 2026 2089 - rswitch_fwd_init(priv); 2027 + err = rswitch_fwd_init(priv); 2028 + if (err < 0) 2029 + goto err_fwd_init; 2090 2030 2091 2031 err = rcar_gen4_ptp_register(priv->ptp_priv, RCAR_GEN4_PTP_REG_LAYOUT, 2092 2032 clk_get_rate(priv->clk)); ··· 2137 2073 err_gwca_request_irq: 2138 2074 rcar_gen4_ptp_unregister(priv->ptp_priv); 2139 2075 2076 + err_fwd_init: 2140 2077 err_ptp_register: 2141 2078 for (i = 0; i < RSWITCH_NUM_PORTS; i++) 2142 2079 rswitch_device_free(priv, i); ··· 2172 2107 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 2173 2108 if (!priv) 2174 2109 return -ENOMEM; 2110 + 2175 2111 spin_lock_init(&priv->lock); 2176 2112 2177 2113 priv->clk = devm_clk_get(&pdev->dev, NULL); ··· 2210 2144 if (!priv->gwca.queues) 2211 2145 return -ENOMEM; 2212 2146 2147 + INIT_LIST_HEAD(&priv->port_list); 2148 + 2213 2149 pm_runtime_enable(&pdev->dev); 2214 2150 pm_runtime_get_sync(&pdev->dev); 2215 2151 ··· 2219 2151 if (ret < 0) { 2220 2152 pm_runtime_put(&pdev->dev); 2221 2153 pm_runtime_disable(&pdev->dev); 2154 + return ret; 2155 + } 2156 + 2157 + if (list_empty(&priv->port_list)) 2158 + dev_warn(&pdev->dev, "could not initialize any ports\n"); 2159 + 2160 + ret = rswitch_register_notifiers(); 2161 + if (ret) { 2162 + dev_err(&pdev->dev, "could not register notifiers\n"); 2222 2163 return ret; 2223 2164 } 2224 2165 ··· 2264 2187 { 2265 2188 struct rswitch_private *priv = platform_get_drvdata(pdev); 2266 2189 2190 + rswitch_unregister_notifiers(); 2267 2191 rswitch_deinit(priv); 2268 2192 2269 2193 pm_runtime_put(&pdev->dev);
+41 -2
drivers/net/ethernet/renesas/rswitch.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Renesas Ethernet Switch device driver 3 3 * 4 - * Copyright (C) 2022 Renesas Electronics Corporation 4 + * Copyright (C) 2022-2025 Renesas Electronics Corporation 5 5 */ 6 6 7 7 #ifndef __RSWITCH_H__ 8 8 #define __RSWITCH_H__ 9 9 10 10 #include <linux/platform_device.h> 11 + #include <linux/phy.h> 12 + 11 13 #include "rcar_gen4_ptp.h" 12 14 13 15 #define RSWITCH_MAX_NUM_QUEUES 128 14 16 15 17 #define RSWITCH_NUM_AGENTS 5 16 18 #define RSWITCH_NUM_PORTS 3 19 + 20 + #define rswitch_for_all_ports(_priv, _rdev) \ 21 + list_for_each_entry(_rdev, &_priv->port_list, list) 22 + 17 23 #define rswitch_for_each_enabled_port(priv, i) \ 18 24 for (i = 0; i < RSWITCH_NUM_PORTS; i++) \ 19 25 if (priv->rdev[i]->disabled) \ ··· 815 809 #define FWPC0_IP4EA BIT(10) 816 810 #define FWPC0_IPDSA BIT(12) 817 811 #define FWPC0_IPHLA BIT(18) 818 - #define FWPC0_MACSDA BIT(20) 812 + #define FWPC0_MACDSA BIT(20) 813 + #define FWPC0_MACSSA BIT(23) 819 814 #define FWPC0_MACHLA BIT(26) 820 815 #define FWPC0_MACHMA BIT(27) 821 816 #define FWPC0_VLANSA BIT(28) ··· 827 820 828 821 #define FWPC2(i) (FWPC20 + (i) * 0x10) 829 822 #define FWCP2_LTWFW GENMASK(16 + (RSWITCH_NUM_AGENTS - 1), 16) 823 + #define FWCP2_LTWFW_MASK GENMASK(16 + (RSWITCH_NUM_AGENTS - 1), 16) 830 824 831 825 #define FWPBFC(i) (FWPBFC0 + (i) * 0x10) 832 826 #define FWPBFC_PBDV GENMASK(RSWITCH_NUM_AGENTS - 1, 0) 833 827 834 828 #define FWPBFCSDC(j, i) (FWPBFCSDC00 + (i) * 0x10 + (j) * 0x04) 829 + 830 + #define FWMACHEC_MACHMUE_MASK GENMASK(26, 16) 831 + 832 + #define FWMACTIM_MACTIOG BIT(0) 833 + #define FWMACTIM_MACTR BIT(1) 834 + 835 + #define FWMACAGUSPC_MACAGUSP GENMASK(9, 0) 836 + #define FWMACAGC_MACAGT GENMASK(15, 0) 837 + #define FWMACAGC_MACAGE BIT(16) 838 + #define FWMACAGC_MACAGSL BIT(17) 839 + #define FWMACAGC_MACAGPM BIT(18) 840 + #define FWMACAGC_MACDES BIT(24) 841 + #define FWMACAGC_MACAGOG BIT(28) 842 + #define FWMACAGC_MACDESOG BIT(29) 843 + 844 + #define RSW_AGEING_CLK_PER_US 0x140 845 + #define RSW_AGEING_TIME 300 835 846 836 847 /* TOP */ 837 848 #define TPEMIMC7(queue) (TPEMIMC70 + (queue) * 4) ··· 1019 994 DECLARE_BITMAP(ts_skb_used, TS_TAGS_PER_PORT); 1020 995 bool disabled; 1021 996 997 + struct list_head list; 998 + 1022 999 int port; 1023 1000 struct rswitch_etha *etha; 1024 1001 struct device_node *np_port; 1025 1002 struct phy *serdes; 1003 + 1004 + struct net_device *brdev; /* master bridge device */ 1005 + unsigned int learning_requested : 1; 1006 + unsigned int learning_offloaded : 1; 1007 + unsigned int forwarding_requested : 1; 1008 + unsigned int forwarding_offloaded : 1; 1026 1009 }; 1027 1010 1028 1011 struct rswitch_mfwd_mac_table_entry { ··· 1055 1022 struct rswitch_etha etha[RSWITCH_NUM_PORTS]; 1056 1023 struct rswitch_mfwd mfwd; 1057 1024 1025 + struct list_head port_list; 1026 + 1058 1027 spinlock_t lock; /* lock interrupt registers' control */ 1059 1028 struct clk *clk; 1060 1029 1061 1030 bool etha_no_runtime_change; 1062 1031 bool gwca_halt; 1032 + struct net_device *offload_brdev; 1063 1033 }; 1034 + 1035 + bool is_rdev(const struct net_device *ndev); 1036 + void rswitch_modify(void __iomem *addr, enum rswitch_reg reg, u32 clear, u32 set); 1064 1037 1065 1038 #endif /* #ifndef __RSWITCH_H__ */
+316
drivers/net/ethernet/renesas/rswitch_l2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Renesas Ethernet Switch device driver 3 + * 4 + * Copyright (C) 2025 Renesas Electronics Corporation 5 + */ 6 + 7 + #include <linux/err.h> 8 + #include <linux/etherdevice.h> 9 + #include <linux/if_bridge.h> 10 + #include <linux/kernel.h> 11 + #include <net/switchdev.h> 12 + 13 + #include "rswitch.h" 14 + #include "rswitch_l2.h" 15 + 16 + static bool rdev_for_l2_offload(struct rswitch_device *rdev) 17 + { 18 + return rdev->priv->offload_brdev && 19 + rdev->brdev == rdev->priv->offload_brdev && 20 + (test_bit(rdev->port, rdev->priv->opened_ports)); 21 + } 22 + 23 + static void rswitch_change_l2_hw_offloading(struct rswitch_device *rdev, 24 + bool start, bool learning) 25 + { 26 + u32 bits = learning ? FWPC0_MACSSA | FWPC0_MACHLA | FWPC0_MACHMA : FWPC0_MACDSA; 27 + u32 clear = start ? 0 : bits; 28 + u32 set = start ? bits : 0; 29 + 30 + if ((learning && rdev->learning_offloaded == start) || 31 + (!learning && rdev->forwarding_offloaded == start)) 32 + return; 33 + 34 + rswitch_modify(rdev->priv->addr, FWPC0(rdev->port), clear, set); 35 + 36 + if (learning) 37 + rdev->learning_offloaded = start; 38 + else 39 + rdev->forwarding_offloaded = start; 40 + 41 + netdev_info(rdev->ndev, "%s hw %s\n", start ? "starting" : "stopping", 42 + learning ? "learning" : "forwarding"); 43 + } 44 + 45 + static void rswitch_update_l2_hw_learning(struct rswitch_private *priv) 46 + { 47 + struct rswitch_device *rdev; 48 + bool learning_needed; 49 + 50 + rswitch_for_all_ports(priv, rdev) { 51 + if (rdev_for_l2_offload(rdev)) 52 + learning_needed = rdev->learning_requested; 53 + else 54 + learning_needed = false; 55 + 56 + rswitch_change_l2_hw_offloading(rdev, learning_needed, true); 57 + } 58 + } 59 + 60 + static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv) 61 + { 62 + struct rswitch_device *rdev; 63 + unsigned int fwd_mask; 64 + 65 + /* calculate fwd_mask with zeroes in bits corresponding to ports that 66 + * shall participate in hardware forwarding 67 + */ 68 + fwd_mask = GENMASK(RSWITCH_NUM_AGENTS - 1, 0); 69 + 70 + rswitch_for_all_ports(priv, rdev) { 71 + if (rdev_for_l2_offload(rdev) && rdev->forwarding_requested) 72 + fwd_mask &= ~BIT(rdev->port); 73 + } 74 + 75 + rswitch_for_all_ports(priv, rdev) { 76 + if ((rdev_for_l2_offload(rdev) && rdev->forwarding_requested) || 77 + rdev->forwarding_offloaded) { 78 + /* Update allowed offload destinations even for ports 79 + * with L2 offload enabled earlier. 80 + * 81 + * Do not allow L2 forwarding to self for hw port. 82 + */ 83 + iowrite32(FIELD_PREP(FWCP2_LTWFW_MASK, fwd_mask | BIT(rdev->port)), 84 + priv->addr + FWPC2(rdev->port)); 85 + } 86 + 87 + if (rdev_for_l2_offload(rdev) && 88 + rdev->forwarding_requested && 89 + !rdev->forwarding_offloaded) { 90 + rswitch_change_l2_hw_offloading(rdev, true, false); 91 + } else if (rdev->forwarding_offloaded) { 92 + rswitch_change_l2_hw_offloading(rdev, false, false); 93 + } 94 + } 95 + } 96 + 97 + void rswitch_update_l2_offload(struct rswitch_private *priv) 98 + { 99 + rswitch_update_l2_hw_learning(priv); 100 + rswitch_update_l2_hw_forwarding(priv); 101 + } 102 + 103 + static void rswitch_update_offload_brdev(struct rswitch_private *priv) 104 + { 105 + struct net_device *offload_brdev = NULL; 106 + struct rswitch_device *rdev, *rdev2; 107 + 108 + rswitch_for_all_ports(priv, rdev) { 109 + if (!rdev->brdev) 110 + continue; 111 + rswitch_for_all_ports(priv, rdev2) { 112 + if (rdev2 == rdev) 113 + break; 114 + if (rdev2->brdev == rdev->brdev) { 115 + offload_brdev = rdev->brdev; 116 + break; 117 + } 118 + } 119 + if (offload_brdev) 120 + break; 121 + } 122 + 123 + if (offload_brdev == priv->offload_brdev) 124 + dev_dbg(&priv->pdev->dev, 125 + "changing l2 offload from %s to %s\n", 126 + netdev_name(priv->offload_brdev), 127 + netdev_name(offload_brdev)); 128 + else if (offload_brdev) 129 + dev_dbg(&priv->pdev->dev, "starting l2 offload for %s\n", 130 + netdev_name(offload_brdev)); 131 + else if (!offload_brdev) 132 + dev_dbg(&priv->pdev->dev, "stopping l2 offload for %s\n", 133 + netdev_name(priv->offload_brdev)); 134 + 135 + priv->offload_brdev = offload_brdev; 136 + 137 + rswitch_update_l2_offload(priv); 138 + } 139 + 140 + static bool rswitch_port_check(const struct net_device *ndev) 141 + { 142 + return is_rdev(ndev); 143 + } 144 + 145 + static void rswitch_port_update_brdev(struct net_device *ndev, 146 + struct net_device *brdev) 147 + { 148 + struct rswitch_device *rdev; 149 + 150 + if (!is_rdev(ndev)) 151 + return; 152 + 153 + rdev = netdev_priv(ndev); 154 + rdev->brdev = brdev; 155 + rswitch_update_offload_brdev(rdev->priv); 156 + } 157 + 158 + static int rswitch_port_update_stp_state(struct net_device *ndev, u8 stp_state) 159 + { 160 + struct rswitch_device *rdev; 161 + 162 + if (!is_rdev(ndev)) 163 + return -ENODEV; 164 + 165 + rdev = netdev_priv(ndev); 166 + rdev->learning_requested = (stp_state == BR_STATE_LEARNING || 167 + stp_state == BR_STATE_FORWARDING); 168 + rdev->forwarding_requested = (stp_state == BR_STATE_FORWARDING); 169 + rswitch_update_l2_offload(rdev->priv); 170 + 171 + return 0; 172 + } 173 + 174 + static int rswitch_netdevice_event(struct notifier_block *nb, 175 + unsigned long event, void *ptr) 176 + { 177 + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); 178 + struct netdev_notifier_changeupper_info *info; 179 + struct net_device *brdev; 180 + 181 + if (!rswitch_port_check(ndev)) 182 + return NOTIFY_DONE; 183 + if (event != NETDEV_CHANGEUPPER) 184 + return NOTIFY_DONE; 185 + 186 + info = ptr; 187 + 188 + if (netif_is_bridge_master(info->upper_dev)) { 189 + brdev = info->linking ? info->upper_dev : NULL; 190 + rswitch_port_update_brdev(ndev, brdev); 191 + } 192 + 193 + return NOTIFY_OK; 194 + } 195 + 196 + static int rswitch_update_ageing_time(struct net_device *ndev, clock_t time) 197 + { 198 + struct rswitch_device *rdev = netdev_priv(ndev); 199 + u32 reg_val; 200 + 201 + if (!is_rdev(ndev)) 202 + return -ENODEV; 203 + 204 + if (!FIELD_FIT(FWMACAGC_MACAGT, time)) 205 + return -EINVAL; 206 + 207 + reg_val = FIELD_PREP(FWMACAGC_MACAGT, time); 208 + reg_val |= FWMACAGC_MACAGE | FWMACAGC_MACAGSL; 209 + iowrite32(reg_val, rdev->priv->addr + FWMACAGC); 210 + 211 + return 0; 212 + } 213 + 214 + static int rswitch_port_attr_set(struct net_device *ndev, const void *ctx, 215 + const struct switchdev_attr *attr, 216 + struct netlink_ext_ack *extack) 217 + { 218 + switch (attr->id) { 219 + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 220 + return rswitch_port_update_stp_state(ndev, attr->u.stp_state); 221 + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 222 + return rswitch_update_ageing_time(ndev, attr->u.ageing_time); 223 + default: 224 + return -EOPNOTSUPP; 225 + } 226 + } 227 + 228 + static int rswitch_switchdev_event(struct notifier_block *nb, 229 + unsigned long event, void *ptr) 230 + { 231 + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); 232 + int ret; 233 + 234 + if (event == SWITCHDEV_PORT_ATTR_SET) { 235 + ret = switchdev_handle_port_attr_set(ndev, ptr, 236 + rswitch_port_check, 237 + rswitch_port_attr_set); 238 + return notifier_from_errno(ret); 239 + } 240 + 241 + if (!rswitch_port_check(ndev)) 242 + return NOTIFY_DONE; 243 + 244 + return notifier_from_errno(-EOPNOTSUPP); 245 + } 246 + 247 + static int rswitch_switchdev_blocking_event(struct notifier_block *nb, 248 + unsigned long event, void *ptr) 249 + { 250 + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); 251 + int ret; 252 + 253 + switch (event) { 254 + case SWITCHDEV_PORT_OBJ_ADD: 255 + return -EOPNOTSUPP; 256 + case SWITCHDEV_PORT_OBJ_DEL: 257 + return -EOPNOTSUPP; 258 + case SWITCHDEV_PORT_ATTR_SET: 259 + ret = switchdev_handle_port_attr_set(ndev, ptr, 260 + rswitch_port_check, 261 + rswitch_port_attr_set); 262 + break; 263 + default: 264 + if (!rswitch_port_check(ndev)) 265 + return NOTIFY_DONE; 266 + ret = -EOPNOTSUPP; 267 + } 268 + 269 + return notifier_from_errno(ret); 270 + } 271 + 272 + static struct notifier_block rswitch_netdevice_nb = { 273 + .notifier_call = rswitch_netdevice_event, 274 + }; 275 + 276 + static struct notifier_block rswitch_switchdev_nb = { 277 + .notifier_call = rswitch_switchdev_event, 278 + }; 279 + 280 + static struct notifier_block rswitch_switchdev_blocking_nb = { 281 + .notifier_call = rswitch_switchdev_blocking_event, 282 + }; 283 + 284 + int rswitch_register_notifiers(void) 285 + { 286 + int ret; 287 + 288 + ret = register_netdevice_notifier(&rswitch_netdevice_nb); 289 + if (ret) 290 + goto register_netdevice_notifier_failed; 291 + 292 + ret = register_switchdev_notifier(&rswitch_switchdev_nb); 293 + if (ret) 294 + goto register_switchdev_notifier_failed; 295 + 296 + ret = register_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb); 297 + if (ret) 298 + goto register_switchdev_blocking_notifier_failed; 299 + 300 + return 0; 301 + 302 + register_switchdev_blocking_notifier_failed: 303 + unregister_switchdev_notifier(&rswitch_switchdev_nb); 304 + register_switchdev_notifier_failed: 305 + unregister_netdevice_notifier(&rswitch_netdevice_nb); 306 + register_netdevice_notifier_failed: 307 + 308 + return ret; 309 + } 310 + 311 + void rswitch_unregister_notifiers(void) 312 + { 313 + unregister_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb); 314 + unregister_switchdev_notifier(&rswitch_switchdev_nb); 315 + unregister_netdevice_notifier(&rswitch_netdevice_nb); 316 + }
+15
drivers/net/ethernet/renesas/rswitch_l2.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Renesas Ethernet Switch device driver 3 + * 4 + * Copyright (C) 2025 Renesas Electronics Corporation 5 + */ 6 + 7 + #ifndef __RSWITCH_L2_H__ 8 + #define __RSWITCH_L2_H__ 9 + 10 + void rswitch_update_l2_offload(struct rswitch_private *priv); 11 + 12 + int rswitch_register_notifiers(void); 13 + void rswitch_unregister_notifiers(void); 14 + 15 + #endif /* #ifndef __RSWITCH_L2_H__ */