1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Texas Instruments switchdev Driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2019 Texas Instruments
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/etherdevice.h>
10*4882a593Smuzhiyun #include <linux/if_bridge.h>
11*4882a593Smuzhiyun #include <linux/netdevice.h>
12*4882a593Smuzhiyun #include <linux/workqueue.h>
13*4882a593Smuzhiyun #include <net/switchdev.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include "cpsw.h"
16*4882a593Smuzhiyun #include "cpsw_ale.h"
17*4882a593Smuzhiyun #include "cpsw_priv.h"
18*4882a593Smuzhiyun #include "cpsw_switchdev.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun struct cpsw_switchdev_event_work {
21*4882a593Smuzhiyun struct work_struct work;
22*4882a593Smuzhiyun struct switchdev_notifier_fdb_info fdb_info;
23*4882a593Smuzhiyun struct cpsw_priv *priv;
24*4882a593Smuzhiyun unsigned long event;
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun
cpsw_port_stp_state_set(struct cpsw_priv * priv,struct switchdev_trans * trans,u8 state)27*4882a593Smuzhiyun static int cpsw_port_stp_state_set(struct cpsw_priv *priv,
28*4882a593Smuzhiyun struct switchdev_trans *trans, u8 state)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
31*4882a593Smuzhiyun u8 cpsw_state;
32*4882a593Smuzhiyun int ret = 0;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun if (switchdev_trans_ph_prepare(trans))
35*4882a593Smuzhiyun return 0;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun switch (state) {
38*4882a593Smuzhiyun case BR_STATE_FORWARDING:
39*4882a593Smuzhiyun cpsw_state = ALE_PORT_STATE_FORWARD;
40*4882a593Smuzhiyun break;
41*4882a593Smuzhiyun case BR_STATE_LEARNING:
42*4882a593Smuzhiyun cpsw_state = ALE_PORT_STATE_LEARN;
43*4882a593Smuzhiyun break;
44*4882a593Smuzhiyun case BR_STATE_DISABLED:
45*4882a593Smuzhiyun cpsw_state = ALE_PORT_STATE_DISABLE;
46*4882a593Smuzhiyun break;
47*4882a593Smuzhiyun case BR_STATE_LISTENING:
48*4882a593Smuzhiyun case BR_STATE_BLOCKING:
49*4882a593Smuzhiyun cpsw_state = ALE_PORT_STATE_BLOCK;
50*4882a593Smuzhiyun break;
51*4882a593Smuzhiyun default:
52*4882a593Smuzhiyun return -EOPNOTSUPP;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun ret = cpsw_ale_control_set(cpsw->ale, priv->emac_port,
56*4882a593Smuzhiyun ALE_PORT_STATE, cpsw_state);
57*4882a593Smuzhiyun dev_dbg(priv->dev, "ale state: %u\n", cpsw_state);
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun return ret;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
cpsw_port_attr_br_flags_set(struct cpsw_priv * priv,struct switchdev_trans * trans,struct net_device * orig_dev,unsigned long brport_flags)62*4882a593Smuzhiyun static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
63*4882a593Smuzhiyun struct switchdev_trans *trans,
64*4882a593Smuzhiyun struct net_device *orig_dev,
65*4882a593Smuzhiyun unsigned long brport_flags)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
68*4882a593Smuzhiyun bool unreg_mcast_add = false;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun if (switchdev_trans_ph_prepare(trans))
71*4882a593Smuzhiyun return 0;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun if (brport_flags & BR_MCAST_FLOOD)
74*4882a593Smuzhiyun unreg_mcast_add = true;
75*4882a593Smuzhiyun dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n",
76*4882a593Smuzhiyun unreg_mcast_add, priv->emac_port);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun cpsw_ale_set_unreg_mcast(cpsw->ale, BIT(priv->emac_port),
79*4882a593Smuzhiyun unreg_mcast_add);
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun return 0;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
cpsw_port_attr_br_flags_pre_set(struct net_device * netdev,struct switchdev_trans * trans,unsigned long flags)84*4882a593Smuzhiyun static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
85*4882a593Smuzhiyun struct switchdev_trans *trans,
86*4882a593Smuzhiyun unsigned long flags)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD))
89*4882a593Smuzhiyun return -EINVAL;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun return 0;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
cpsw_port_attr_set(struct net_device * ndev,const struct switchdev_attr * attr,struct switchdev_trans * trans)94*4882a593Smuzhiyun static int cpsw_port_attr_set(struct net_device *ndev,
95*4882a593Smuzhiyun const struct switchdev_attr *attr,
96*4882a593Smuzhiyun struct switchdev_trans *trans)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct cpsw_priv *priv = netdev_priv(ndev);
99*4882a593Smuzhiyun int ret;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun dev_dbg(priv->dev, "attr: id %u port: %u\n", attr->id, priv->emac_port);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun switch (attr->id) {
104*4882a593Smuzhiyun case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
105*4882a593Smuzhiyun ret = cpsw_port_attr_br_flags_pre_set(ndev, trans,
106*4882a593Smuzhiyun attr->u.brport_flags);
107*4882a593Smuzhiyun break;
108*4882a593Smuzhiyun case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
109*4882a593Smuzhiyun ret = cpsw_port_stp_state_set(priv, trans, attr->u.stp_state);
110*4882a593Smuzhiyun dev_dbg(priv->dev, "stp state: %u\n", attr->u.stp_state);
111*4882a593Smuzhiyun break;
112*4882a593Smuzhiyun case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
113*4882a593Smuzhiyun ret = cpsw_port_attr_br_flags_set(priv, trans, attr->orig_dev,
114*4882a593Smuzhiyun attr->u.brport_flags);
115*4882a593Smuzhiyun break;
116*4882a593Smuzhiyun default:
117*4882a593Smuzhiyun ret = -EOPNOTSUPP;
118*4882a593Smuzhiyun break;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return ret;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
cpsw_get_pvid(struct cpsw_priv * priv)124*4882a593Smuzhiyun static u16 cpsw_get_pvid(struct cpsw_priv *priv)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
127*4882a593Smuzhiyun u32 __iomem *port_vlan_reg;
128*4882a593Smuzhiyun u32 pvid;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun if (priv->emac_port) {
131*4882a593Smuzhiyun int reg = CPSW2_PORT_VLAN;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun if (cpsw->version == CPSW_VERSION_1)
134*4882a593Smuzhiyun reg = CPSW1_PORT_VLAN;
135*4882a593Smuzhiyun pvid = slave_read(cpsw->slaves + (priv->emac_port - 1), reg);
136*4882a593Smuzhiyun } else {
137*4882a593Smuzhiyun port_vlan_reg = &cpsw->host_port_regs->port_vlan;
138*4882a593Smuzhiyun pvid = readl(port_vlan_reg);
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun pvid = pvid & 0xfff;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun return pvid;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
cpsw_set_pvid(struct cpsw_priv * priv,u16 vid,bool cfi,u32 cos)146*4882a593Smuzhiyun static void cpsw_set_pvid(struct cpsw_priv *priv, u16 vid, bool cfi, u32 cos)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
149*4882a593Smuzhiyun void __iomem *port_vlan_reg;
150*4882a593Smuzhiyun u32 pvid;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun pvid = vid;
153*4882a593Smuzhiyun pvid |= cfi ? BIT(12) : 0;
154*4882a593Smuzhiyun pvid |= (cos & 0x7) << 13;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (priv->emac_port) {
157*4882a593Smuzhiyun int reg = CPSW2_PORT_VLAN;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (cpsw->version == CPSW_VERSION_1)
160*4882a593Smuzhiyun reg = CPSW1_PORT_VLAN;
161*4882a593Smuzhiyun /* no barrier */
162*4882a593Smuzhiyun slave_write(cpsw->slaves + (priv->emac_port - 1), pvid, reg);
163*4882a593Smuzhiyun } else {
164*4882a593Smuzhiyun /* CPU port */
165*4882a593Smuzhiyun port_vlan_reg = &cpsw->host_port_regs->port_vlan;
166*4882a593Smuzhiyun writel(pvid, port_vlan_reg);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
cpsw_port_vlan_add(struct cpsw_priv * priv,bool untag,bool pvid,u16 vid,struct net_device * orig_dev)170*4882a593Smuzhiyun static int cpsw_port_vlan_add(struct cpsw_priv *priv, bool untag, bool pvid,
171*4882a593Smuzhiyun u16 vid, struct net_device *orig_dev)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun bool cpu_port = netif_is_bridge_master(orig_dev);
174*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
175*4882a593Smuzhiyun int unreg_mcast_mask = 0;
176*4882a593Smuzhiyun int reg_mcast_mask = 0;
177*4882a593Smuzhiyun int untag_mask = 0;
178*4882a593Smuzhiyun int port_mask;
179*4882a593Smuzhiyun int ret = 0;
180*4882a593Smuzhiyun u32 flags;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (cpu_port) {
183*4882a593Smuzhiyun port_mask = BIT(HOST_PORT_NUM);
184*4882a593Smuzhiyun flags = orig_dev->flags;
185*4882a593Smuzhiyun unreg_mcast_mask = port_mask;
186*4882a593Smuzhiyun } else {
187*4882a593Smuzhiyun port_mask = BIT(priv->emac_port);
188*4882a593Smuzhiyun flags = priv->ndev->flags;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun if (flags & IFF_MULTICAST)
192*4882a593Smuzhiyun reg_mcast_mask = port_mask;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun if (untag)
195*4882a593Smuzhiyun untag_mask = port_mask;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun ret = cpsw_ale_vlan_add_modify(cpsw->ale, vid, port_mask, untag_mask,
198*4882a593Smuzhiyun reg_mcast_mask, unreg_mcast_mask);
199*4882a593Smuzhiyun if (ret) {
200*4882a593Smuzhiyun dev_err(priv->dev, "Unable to add vlan\n");
201*4882a593Smuzhiyun return ret;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun if (cpu_port)
205*4882a593Smuzhiyun cpsw_ale_add_ucast(cpsw->ale, priv->mac_addr,
206*4882a593Smuzhiyun HOST_PORT_NUM, ALE_VLAN, vid);
207*4882a593Smuzhiyun if (!pvid)
208*4882a593Smuzhiyun return ret;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun cpsw_set_pvid(priv, vid, 0, 0);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun dev_dbg(priv->dev, "VID add: %s: vid:%u ports:%X\n",
213*4882a593Smuzhiyun priv->ndev->name, vid, port_mask);
214*4882a593Smuzhiyun return ret;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
cpsw_port_vlan_del(struct cpsw_priv * priv,u16 vid,struct net_device * orig_dev)217*4882a593Smuzhiyun static int cpsw_port_vlan_del(struct cpsw_priv *priv, u16 vid,
218*4882a593Smuzhiyun struct net_device *orig_dev)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun bool cpu_port = netif_is_bridge_master(orig_dev);
221*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
222*4882a593Smuzhiyun int port_mask;
223*4882a593Smuzhiyun int ret = 0;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun if (cpu_port)
226*4882a593Smuzhiyun port_mask = BIT(HOST_PORT_NUM);
227*4882a593Smuzhiyun else
228*4882a593Smuzhiyun port_mask = BIT(priv->emac_port);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun ret = cpsw_ale_del_vlan(cpsw->ale, vid, port_mask);
231*4882a593Smuzhiyun if (ret != 0)
232*4882a593Smuzhiyun return ret;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* We don't care for the return value here, error is returned only if
235*4882a593Smuzhiyun * the unicast entry is not present
236*4882a593Smuzhiyun */
237*4882a593Smuzhiyun if (cpu_port)
238*4882a593Smuzhiyun cpsw_ale_del_ucast(cpsw->ale, priv->mac_addr,
239*4882a593Smuzhiyun HOST_PORT_NUM, ALE_VLAN, vid);
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if (vid == cpsw_get_pvid(priv))
242*4882a593Smuzhiyun cpsw_set_pvid(priv, 0, 0, 0);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun /* We don't care for the return value here, error is returned only if
245*4882a593Smuzhiyun * the multicast entry is not present
246*4882a593Smuzhiyun */
247*4882a593Smuzhiyun cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
248*4882a593Smuzhiyun port_mask, ALE_VLAN, vid);
249*4882a593Smuzhiyun dev_dbg(priv->dev, "VID del: %s: vid:%u ports:%X\n",
250*4882a593Smuzhiyun priv->ndev->name, vid, port_mask);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun return ret;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun
cpsw_port_vlans_add(struct cpsw_priv * priv,const struct switchdev_obj_port_vlan * vlan,struct switchdev_trans * trans)255*4882a593Smuzhiyun static int cpsw_port_vlans_add(struct cpsw_priv *priv,
256*4882a593Smuzhiyun const struct switchdev_obj_port_vlan *vlan,
257*4882a593Smuzhiyun struct switchdev_trans *trans)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
260*4882a593Smuzhiyun struct net_device *orig_dev = vlan->obj.orig_dev;
261*4882a593Smuzhiyun bool cpu_port = netif_is_bridge_master(orig_dev);
262*4882a593Smuzhiyun bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
263*4882a593Smuzhiyun u16 vid;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun dev_dbg(priv->dev, "VID add: %s: vid:%u flags:%X\n",
266*4882a593Smuzhiyun priv->ndev->name, vlan->vid_begin, vlan->flags);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY))
269*4882a593Smuzhiyun return 0;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun if (switchdev_trans_ph_prepare(trans))
272*4882a593Smuzhiyun return 0;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
275*4882a593Smuzhiyun int err;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun err = cpsw_port_vlan_add(priv, untag, pvid, vid, orig_dev);
278*4882a593Smuzhiyun if (err)
279*4882a593Smuzhiyun return err;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
cpsw_port_vlans_del(struct cpsw_priv * priv,const struct switchdev_obj_port_vlan * vlan)285*4882a593Smuzhiyun static int cpsw_port_vlans_del(struct cpsw_priv *priv,
286*4882a593Smuzhiyun const struct switchdev_obj_port_vlan *vlan)
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun struct net_device *orig_dev = vlan->obj.orig_dev;
290*4882a593Smuzhiyun u16 vid;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
293*4882a593Smuzhiyun int err;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun err = cpsw_port_vlan_del(priv, vid, orig_dev);
296*4882a593Smuzhiyun if (err)
297*4882a593Smuzhiyun return err;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun return 0;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
cpsw_port_mdb_add(struct cpsw_priv * priv,struct switchdev_obj_port_mdb * mdb,struct switchdev_trans * trans)303*4882a593Smuzhiyun static int cpsw_port_mdb_add(struct cpsw_priv *priv,
304*4882a593Smuzhiyun struct switchdev_obj_port_mdb *mdb,
305*4882a593Smuzhiyun struct switchdev_trans *trans)
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun struct net_device *orig_dev = mdb->obj.orig_dev;
309*4882a593Smuzhiyun bool cpu_port = netif_is_bridge_master(orig_dev);
310*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
311*4882a593Smuzhiyun int port_mask;
312*4882a593Smuzhiyun int err;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun if (switchdev_trans_ph_prepare(trans))
315*4882a593Smuzhiyun return 0;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun if (cpu_port)
318*4882a593Smuzhiyun port_mask = BIT(HOST_PORT_NUM);
319*4882a593Smuzhiyun else
320*4882a593Smuzhiyun port_mask = BIT(priv->emac_port);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun err = cpsw_ale_add_mcast(cpsw->ale, mdb->addr, port_mask,
323*4882a593Smuzhiyun ALE_VLAN, mdb->vid, 0);
324*4882a593Smuzhiyun dev_dbg(priv->dev, "MDB add: %s: vid %u:%pM ports: %X\n",
325*4882a593Smuzhiyun priv->ndev->name, mdb->vid, mdb->addr, port_mask);
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun return err;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
cpsw_port_mdb_del(struct cpsw_priv * priv,struct switchdev_obj_port_mdb * mdb)330*4882a593Smuzhiyun static int cpsw_port_mdb_del(struct cpsw_priv *priv,
331*4882a593Smuzhiyun struct switchdev_obj_port_mdb *mdb)
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun struct net_device *orig_dev = mdb->obj.orig_dev;
335*4882a593Smuzhiyun bool cpu_port = netif_is_bridge_master(orig_dev);
336*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
337*4882a593Smuzhiyun int del_mask;
338*4882a593Smuzhiyun int err;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun if (cpu_port)
341*4882a593Smuzhiyun del_mask = BIT(HOST_PORT_NUM);
342*4882a593Smuzhiyun else
343*4882a593Smuzhiyun del_mask = BIT(priv->emac_port);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun err = cpsw_ale_del_mcast(cpsw->ale, mdb->addr, del_mask,
346*4882a593Smuzhiyun ALE_VLAN, mdb->vid);
347*4882a593Smuzhiyun dev_dbg(priv->dev, "MDB del: %s: vid %u:%pM ports: %X\n",
348*4882a593Smuzhiyun priv->ndev->name, mdb->vid, mdb->addr, del_mask);
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun return err;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
cpsw_port_obj_add(struct net_device * ndev,const struct switchdev_obj * obj,struct switchdev_trans * trans,struct netlink_ext_ack * extack)353*4882a593Smuzhiyun static int cpsw_port_obj_add(struct net_device *ndev,
354*4882a593Smuzhiyun const struct switchdev_obj *obj,
355*4882a593Smuzhiyun struct switchdev_trans *trans,
356*4882a593Smuzhiyun struct netlink_ext_ack *extack)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
359*4882a593Smuzhiyun struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
360*4882a593Smuzhiyun struct cpsw_priv *priv = netdev_priv(ndev);
361*4882a593Smuzhiyun int err = 0;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun dev_dbg(priv->dev, "obj_add: id %u port: %u\n",
364*4882a593Smuzhiyun obj->id, priv->emac_port);
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun switch (obj->id) {
367*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_PORT_VLAN:
368*4882a593Smuzhiyun err = cpsw_port_vlans_add(priv, vlan, trans);
369*4882a593Smuzhiyun break;
370*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_PORT_MDB:
371*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_HOST_MDB:
372*4882a593Smuzhiyun err = cpsw_port_mdb_add(priv, mdb, trans);
373*4882a593Smuzhiyun break;
374*4882a593Smuzhiyun default:
375*4882a593Smuzhiyun err = -EOPNOTSUPP;
376*4882a593Smuzhiyun break;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun return err;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun
cpsw_port_obj_del(struct net_device * ndev,const struct switchdev_obj * obj)382*4882a593Smuzhiyun static int cpsw_port_obj_del(struct net_device *ndev,
383*4882a593Smuzhiyun const struct switchdev_obj *obj)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
386*4882a593Smuzhiyun struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
387*4882a593Smuzhiyun struct cpsw_priv *priv = netdev_priv(ndev);
388*4882a593Smuzhiyun int err = 0;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun dev_dbg(priv->dev, "obj_del: id %u port: %u\n",
391*4882a593Smuzhiyun obj->id, priv->emac_port);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun switch (obj->id) {
394*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_PORT_VLAN:
395*4882a593Smuzhiyun err = cpsw_port_vlans_del(priv, vlan);
396*4882a593Smuzhiyun break;
397*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_PORT_MDB:
398*4882a593Smuzhiyun case SWITCHDEV_OBJ_ID_HOST_MDB:
399*4882a593Smuzhiyun err = cpsw_port_mdb_del(priv, mdb);
400*4882a593Smuzhiyun break;
401*4882a593Smuzhiyun default:
402*4882a593Smuzhiyun err = -EOPNOTSUPP;
403*4882a593Smuzhiyun break;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun return err;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
cpsw_fdb_offload_notify(struct net_device * ndev,struct switchdev_notifier_fdb_info * rcv)409*4882a593Smuzhiyun static void cpsw_fdb_offload_notify(struct net_device *ndev,
410*4882a593Smuzhiyun struct switchdev_notifier_fdb_info *rcv)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun struct switchdev_notifier_fdb_info info;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun info.addr = rcv->addr;
415*4882a593Smuzhiyun info.vid = rcv->vid;
416*4882a593Smuzhiyun info.offloaded = true;
417*4882a593Smuzhiyun call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
418*4882a593Smuzhiyun ndev, &info.info, NULL);
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
cpsw_switchdev_event_work(struct work_struct * work)421*4882a593Smuzhiyun static void cpsw_switchdev_event_work(struct work_struct *work)
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun struct cpsw_switchdev_event_work *switchdev_work =
424*4882a593Smuzhiyun container_of(work, struct cpsw_switchdev_event_work, work);
425*4882a593Smuzhiyun struct cpsw_priv *priv = switchdev_work->priv;
426*4882a593Smuzhiyun struct switchdev_notifier_fdb_info *fdb;
427*4882a593Smuzhiyun struct cpsw_common *cpsw = priv->cpsw;
428*4882a593Smuzhiyun int port = priv->emac_port;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun rtnl_lock();
431*4882a593Smuzhiyun switch (switchdev_work->event) {
432*4882a593Smuzhiyun case SWITCHDEV_FDB_ADD_TO_DEVICE:
433*4882a593Smuzhiyun fdb = &switchdev_work->fdb_info;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun dev_dbg(cpsw->dev, "cpsw_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n",
436*4882a593Smuzhiyun fdb->addr, fdb->vid, fdb->added_by_user,
437*4882a593Smuzhiyun fdb->offloaded, port);
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun if (!fdb->added_by_user)
440*4882a593Smuzhiyun break;
441*4882a593Smuzhiyun if (memcmp(priv->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
442*4882a593Smuzhiyun port = HOST_PORT_NUM;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun cpsw_ale_add_ucast(cpsw->ale, (u8 *)fdb->addr, port,
445*4882a593Smuzhiyun fdb->vid ? ALE_VLAN : 0, fdb->vid);
446*4882a593Smuzhiyun cpsw_fdb_offload_notify(priv->ndev, fdb);
447*4882a593Smuzhiyun break;
448*4882a593Smuzhiyun case SWITCHDEV_FDB_DEL_TO_DEVICE:
449*4882a593Smuzhiyun fdb = &switchdev_work->fdb_info;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun dev_dbg(cpsw->dev, "cpsw_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n",
452*4882a593Smuzhiyun fdb->addr, fdb->vid, fdb->added_by_user,
453*4882a593Smuzhiyun fdb->offloaded, port);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun if (!fdb->added_by_user)
456*4882a593Smuzhiyun break;
457*4882a593Smuzhiyun if (memcmp(priv->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
458*4882a593Smuzhiyun port = HOST_PORT_NUM;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port,
461*4882a593Smuzhiyun fdb->vid ? ALE_VLAN : 0, fdb->vid);
462*4882a593Smuzhiyun break;
463*4882a593Smuzhiyun default:
464*4882a593Smuzhiyun break;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun rtnl_unlock();
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun kfree(switchdev_work->fdb_info.addr);
469*4882a593Smuzhiyun kfree(switchdev_work);
470*4882a593Smuzhiyun dev_put(priv->ndev);
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun /* called under rcu_read_lock() */
cpsw_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)474*4882a593Smuzhiyun static int cpsw_switchdev_event(struct notifier_block *unused,
475*4882a593Smuzhiyun unsigned long event, void *ptr)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
478*4882a593Smuzhiyun struct switchdev_notifier_fdb_info *fdb_info = ptr;
479*4882a593Smuzhiyun struct cpsw_switchdev_event_work *switchdev_work;
480*4882a593Smuzhiyun struct cpsw_priv *priv = netdev_priv(ndev);
481*4882a593Smuzhiyun int err;
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun if (event == SWITCHDEV_PORT_ATTR_SET) {
484*4882a593Smuzhiyun err = switchdev_handle_port_attr_set(ndev, ptr,
485*4882a593Smuzhiyun cpsw_port_dev_check,
486*4882a593Smuzhiyun cpsw_port_attr_set);
487*4882a593Smuzhiyun return notifier_from_errno(err);
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun if (!cpsw_port_dev_check(ndev))
491*4882a593Smuzhiyun return NOTIFY_DONE;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
494*4882a593Smuzhiyun if (WARN_ON(!switchdev_work))
495*4882a593Smuzhiyun return NOTIFY_BAD;
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun INIT_WORK(&switchdev_work->work, cpsw_switchdev_event_work);
498*4882a593Smuzhiyun switchdev_work->priv = priv;
499*4882a593Smuzhiyun switchdev_work->event = event;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun switch (event) {
502*4882a593Smuzhiyun case SWITCHDEV_FDB_ADD_TO_DEVICE:
503*4882a593Smuzhiyun case SWITCHDEV_FDB_DEL_TO_DEVICE:
504*4882a593Smuzhiyun memcpy(&switchdev_work->fdb_info, ptr,
505*4882a593Smuzhiyun sizeof(switchdev_work->fdb_info));
506*4882a593Smuzhiyun switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
507*4882a593Smuzhiyun if (!switchdev_work->fdb_info.addr)
508*4882a593Smuzhiyun goto err_addr_alloc;
509*4882a593Smuzhiyun ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
510*4882a593Smuzhiyun fdb_info->addr);
511*4882a593Smuzhiyun dev_hold(ndev);
512*4882a593Smuzhiyun break;
513*4882a593Smuzhiyun default:
514*4882a593Smuzhiyun kfree(switchdev_work);
515*4882a593Smuzhiyun return NOTIFY_DONE;
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun queue_work(system_long_wq, &switchdev_work->work);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun return NOTIFY_DONE;
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun err_addr_alloc:
523*4882a593Smuzhiyun kfree(switchdev_work);
524*4882a593Smuzhiyun return NOTIFY_BAD;
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun static struct notifier_block cpsw_switchdev_notifier = {
528*4882a593Smuzhiyun .notifier_call = cpsw_switchdev_event,
529*4882a593Smuzhiyun };
530*4882a593Smuzhiyun
cpsw_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)531*4882a593Smuzhiyun static int cpsw_switchdev_blocking_event(struct notifier_block *unused,
532*4882a593Smuzhiyun unsigned long event, void *ptr)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
535*4882a593Smuzhiyun int err;
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun switch (event) {
538*4882a593Smuzhiyun case SWITCHDEV_PORT_OBJ_ADD:
539*4882a593Smuzhiyun err = switchdev_handle_port_obj_add(dev, ptr,
540*4882a593Smuzhiyun cpsw_port_dev_check,
541*4882a593Smuzhiyun cpsw_port_obj_add);
542*4882a593Smuzhiyun return notifier_from_errno(err);
543*4882a593Smuzhiyun case SWITCHDEV_PORT_OBJ_DEL:
544*4882a593Smuzhiyun err = switchdev_handle_port_obj_del(dev, ptr,
545*4882a593Smuzhiyun cpsw_port_dev_check,
546*4882a593Smuzhiyun cpsw_port_obj_del);
547*4882a593Smuzhiyun return notifier_from_errno(err);
548*4882a593Smuzhiyun case SWITCHDEV_PORT_ATTR_SET:
549*4882a593Smuzhiyun err = switchdev_handle_port_attr_set(dev, ptr,
550*4882a593Smuzhiyun cpsw_port_dev_check,
551*4882a593Smuzhiyun cpsw_port_attr_set);
552*4882a593Smuzhiyun return notifier_from_errno(err);
553*4882a593Smuzhiyun default:
554*4882a593Smuzhiyun break;
555*4882a593Smuzhiyun }
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun return NOTIFY_DONE;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun static struct notifier_block cpsw_switchdev_bl_notifier = {
561*4882a593Smuzhiyun .notifier_call = cpsw_switchdev_blocking_event,
562*4882a593Smuzhiyun };
563*4882a593Smuzhiyun
cpsw_switchdev_register_notifiers(struct cpsw_common * cpsw)564*4882a593Smuzhiyun int cpsw_switchdev_register_notifiers(struct cpsw_common *cpsw)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun int ret = 0;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun ret = register_switchdev_notifier(&cpsw_switchdev_notifier);
569*4882a593Smuzhiyun if (ret) {
570*4882a593Smuzhiyun dev_err(cpsw->dev, "register switchdev notifier fail ret:%d\n",
571*4882a593Smuzhiyun ret);
572*4882a593Smuzhiyun return ret;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun ret = register_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier);
576*4882a593Smuzhiyun if (ret) {
577*4882a593Smuzhiyun dev_err(cpsw->dev, "register switchdev blocking notifier ret:%d\n",
578*4882a593Smuzhiyun ret);
579*4882a593Smuzhiyun unregister_switchdev_notifier(&cpsw_switchdev_notifier);
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun return ret;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun
cpsw_switchdev_unregister_notifiers(struct cpsw_common * cpsw)585*4882a593Smuzhiyun void cpsw_switchdev_unregister_notifiers(struct cpsw_common *cpsw)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun unregister_switchdev_blocking_notifier(&cpsw_switchdev_bl_notifier);
588*4882a593Smuzhiyun unregister_switchdev_notifier(&cpsw_switchdev_notifier);
589*4882a593Smuzhiyun }
590