1eea65b87SRoger Quadros // SPDX-License-Identifier: GPL-2.0
2eea65b87SRoger Quadros
3eea65b87SRoger Quadros /* Texas Instruments ICSSM Ethernet Driver
4eea65b87SRoger Quadros *
5eea65b87SRoger Quadros * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
6eea65b87SRoger Quadros *
7eea65b87SRoger Quadros */
8eea65b87SRoger Quadros
9eea65b87SRoger Quadros #include <linux/etherdevice.h>
10eea65b87SRoger Quadros #include <linux/kernel.h>
11eea65b87SRoger Quadros #include <linux/remoteproc.h>
12eea65b87SRoger Quadros #include <net/switchdev.h>
13eea65b87SRoger Quadros
14eea65b87SRoger Quadros #include "icssm_prueth.h"
15eea65b87SRoger Quadros #include "icssm_prueth_switch.h"
16eea65b87SRoger Quadros #include "icssm_prueth_fdb_tbl.h"
17eea65b87SRoger Quadros
18eea65b87SRoger Quadros /* switchev event work */
19eea65b87SRoger Quadros struct icssm_sw_event_work {
20eea65b87SRoger Quadros netdevice_tracker ndev_tracker;
21eea65b87SRoger Quadros struct work_struct work;
22eea65b87SRoger Quadros struct switchdev_notifier_fdb_info fdb_info;
23eea65b87SRoger Quadros struct prueth_emac *emac;
24eea65b87SRoger Quadros unsigned long event;
25eea65b87SRoger Quadros };
26eea65b87SRoger Quadros
icssm_prueth_sw_set_stp_state(struct prueth * prueth,enum prueth_port port,u8 state)27eea65b87SRoger Quadros void icssm_prueth_sw_set_stp_state(struct prueth *prueth,
28eea65b87SRoger Quadros enum prueth_port port, u8 state)
29eea65b87SRoger Quadros {
30eea65b87SRoger Quadros struct fdb_tbl *t = prueth->fdb_tbl;
31eea65b87SRoger Quadros
32eea65b87SRoger Quadros writeb(state, port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
33eea65b87SRoger Quadros (void __iomem *)&t->port1_stp_cfg->state);
34eea65b87SRoger Quadros }
35eea65b87SRoger Quadros
icssm_prueth_sw_get_stp_state(struct prueth * prueth,enum prueth_port port)36eea65b87SRoger Quadros u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth, enum prueth_port port)
37eea65b87SRoger Quadros {
38eea65b87SRoger Quadros struct fdb_tbl *t = prueth->fdb_tbl;
39eea65b87SRoger Quadros u8 state;
40eea65b87SRoger Quadros
41eea65b87SRoger Quadros state = readb(port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
42eea65b87SRoger Quadros (void __iomem *)&t->port1_stp_cfg->state);
43eea65b87SRoger Quadros return state;
44eea65b87SRoger Quadros }
45eea65b87SRoger Quadros
icssm_prueth_sw_attr_set(struct net_device * ndev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)46eea65b87SRoger Quadros static int icssm_prueth_sw_attr_set(struct net_device *ndev, const void *ctx,
47eea65b87SRoger Quadros const struct switchdev_attr *attr,
48eea65b87SRoger Quadros struct netlink_ext_ack *extack)
49eea65b87SRoger Quadros {
50eea65b87SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev);
51eea65b87SRoger Quadros struct prueth *prueth = emac->prueth;
52eea65b87SRoger Quadros int err = 0;
53eea65b87SRoger Quadros u8 o_state;
54eea65b87SRoger Quadros
55eea65b87SRoger Quadros /* Interface is not up */
56eea65b87SRoger Quadros if (!prueth->fdb_tbl)
57eea65b87SRoger Quadros return 0;
58eea65b87SRoger Quadros
59eea65b87SRoger Quadros switch (attr->id) {
60eea65b87SRoger Quadros case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
61eea65b87SRoger Quadros o_state = icssm_prueth_sw_get_stp_state(prueth, emac->port_id);
62eea65b87SRoger Quadros icssm_prueth_sw_set_stp_state(prueth, emac->port_id,
63eea65b87SRoger Quadros attr->u.stp_state);
64eea65b87SRoger Quadros
65eea65b87SRoger Quadros if (o_state != attr->u.stp_state)
66eea65b87SRoger Quadros icssm_prueth_sw_purge_fdb(emac);
67eea65b87SRoger Quadros
68eea65b87SRoger Quadros dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n",
69eea65b87SRoger Quadros attr->u.stp_state, emac->port_id);
70eea65b87SRoger Quadros break;
71eea65b87SRoger Quadros default:
72eea65b87SRoger Quadros err = -EOPNOTSUPP;
73eea65b87SRoger Quadros break;
74eea65b87SRoger Quadros }
75eea65b87SRoger Quadros
76eea65b87SRoger Quadros return err;
77eea65b87SRoger Quadros }
78eea65b87SRoger Quadros
icssm_prueth_sw_fdb_offload(struct net_device * ndev,struct switchdev_notifier_fdb_info * rcv)79eea65b87SRoger Quadros static void icssm_prueth_sw_fdb_offload(struct net_device *ndev,
80eea65b87SRoger Quadros struct switchdev_notifier_fdb_info *rcv)
81eea65b87SRoger Quadros {
82eea65b87SRoger Quadros struct switchdev_notifier_fdb_info info;
83eea65b87SRoger Quadros
84eea65b87SRoger Quadros info.addr = rcv->addr;
85eea65b87SRoger Quadros info.vid = rcv->vid;
86eea65b87SRoger Quadros call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info,
87eea65b87SRoger Quadros NULL);
88eea65b87SRoger Quadros }
89eea65b87SRoger Quadros
90eea65b87SRoger Quadros /**
91eea65b87SRoger Quadros * icssm_sw_event_work - insert/delete fdb entry
92eea65b87SRoger Quadros *
93eea65b87SRoger Quadros * @work: work structure
94eea65b87SRoger Quadros *
95eea65b87SRoger Quadros */
icssm_sw_event_work(struct work_struct * work)96eea65b87SRoger Quadros static void icssm_sw_event_work(struct work_struct *work)
97eea65b87SRoger Quadros {
98eea65b87SRoger Quadros struct icssm_sw_event_work *switchdev_work =
99eea65b87SRoger Quadros container_of(work, struct icssm_sw_event_work, work);
100eea65b87SRoger Quadros struct prueth_emac *emac = switchdev_work->emac;
101eea65b87SRoger Quadros struct switchdev_notifier_fdb_info *fdb;
102eea65b87SRoger Quadros struct prueth *prueth = emac->prueth;
103eea65b87SRoger Quadros int port = emac->port_id;
104eea65b87SRoger Quadros
105eea65b87SRoger Quadros rtnl_lock();
106eea65b87SRoger Quadros
107eea65b87SRoger Quadros /* Interface is not up */
108eea65b87SRoger Quadros if (!emac->prueth->fdb_tbl)
109eea65b87SRoger Quadros goto free;
110eea65b87SRoger Quadros
111eea65b87SRoger Quadros switch (switchdev_work->event) {
112eea65b87SRoger Quadros case SWITCHDEV_FDB_ADD_TO_DEVICE:
113eea65b87SRoger Quadros fdb = &switchdev_work->fdb_info;
114eea65b87SRoger Quadros dev_dbg(prueth->dev,
115eea65b87SRoger Quadros "prueth fdb add: MACID = %pM vid = %u flags = %u -- port %d\n",
116eea65b87SRoger Quadros fdb->addr, fdb->vid, fdb->added_by_user, port);
117eea65b87SRoger Quadros
118eea65b87SRoger Quadros if (!fdb->added_by_user)
119eea65b87SRoger Quadros break;
120eea65b87SRoger Quadros
121eea65b87SRoger Quadros if (fdb->is_local)
122eea65b87SRoger Quadros break;
123eea65b87SRoger Quadros
124eea65b87SRoger Quadros icssm_prueth_sw_fdb_add(emac, fdb);
125eea65b87SRoger Quadros icssm_prueth_sw_fdb_offload(emac->ndev, fdb);
126eea65b87SRoger Quadros break;
127eea65b87SRoger Quadros case SWITCHDEV_FDB_DEL_TO_DEVICE:
128eea65b87SRoger Quadros fdb = &switchdev_work->fdb_info;
129eea65b87SRoger Quadros dev_dbg(prueth->dev,
130eea65b87SRoger Quadros "prueth fdb del: MACID = %pM vid = %u flags = %u -- port %d\n",
131eea65b87SRoger Quadros fdb->addr, fdb->vid, fdb->added_by_user, port);
132eea65b87SRoger Quadros
133eea65b87SRoger Quadros if (fdb->is_local)
134eea65b87SRoger Quadros break;
135eea65b87SRoger Quadros
136eea65b87SRoger Quadros icssm_prueth_sw_fdb_del(emac, fdb);
137eea65b87SRoger Quadros break;
138eea65b87SRoger Quadros default:
139eea65b87SRoger Quadros break;
140eea65b87SRoger Quadros }
141eea65b87SRoger Quadros
142eea65b87SRoger Quadros free:
143eea65b87SRoger Quadros rtnl_unlock();
144eea65b87SRoger Quadros
145eea65b87SRoger Quadros netdev_put(emac->ndev, &switchdev_work->ndev_tracker);
146eea65b87SRoger Quadros kfree(switchdev_work->fdb_info.addr);
147eea65b87SRoger Quadros kfree(switchdev_work);
148eea65b87SRoger Quadros }
149eea65b87SRoger Quadros
150eea65b87SRoger Quadros /* called under rcu_read_lock() */
icssm_prueth_sw_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)151eea65b87SRoger Quadros static int icssm_prueth_sw_switchdev_event(struct notifier_block *unused,
152eea65b87SRoger Quadros unsigned long event, void *ptr)
153eea65b87SRoger Quadros {
154eea65b87SRoger Quadros struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
155eea65b87SRoger Quadros struct switchdev_notifier_fdb_info *fdb_info = ptr;
156eea65b87SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev);
157eea65b87SRoger Quadros struct icssm_sw_event_work *switchdev_work;
158eea65b87SRoger Quadros int err;
159eea65b87SRoger Quadros
160eea65b87SRoger Quadros if (!icssm_prueth_sw_port_dev_check(ndev))
161eea65b87SRoger Quadros return NOTIFY_DONE;
162eea65b87SRoger Quadros
163eea65b87SRoger Quadros if (event == SWITCHDEV_PORT_ATTR_SET) {
164eea65b87SRoger Quadros err = switchdev_handle_port_attr_set
165eea65b87SRoger Quadros (ndev, ptr, icssm_prueth_sw_port_dev_check,
166eea65b87SRoger Quadros icssm_prueth_sw_attr_set);
167eea65b87SRoger Quadros return notifier_from_errno(err);
168eea65b87SRoger Quadros }
169eea65b87SRoger Quadros
170*69050f8dSKees Cook switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
171eea65b87SRoger Quadros if (WARN_ON(!switchdev_work))
172eea65b87SRoger Quadros return NOTIFY_BAD;
173eea65b87SRoger Quadros
174eea65b87SRoger Quadros INIT_WORK(&switchdev_work->work, icssm_sw_event_work);
175eea65b87SRoger Quadros switchdev_work->emac = emac;
176eea65b87SRoger Quadros switchdev_work->event = event;
177eea65b87SRoger Quadros
178eea65b87SRoger Quadros switch (event) {
179eea65b87SRoger Quadros case SWITCHDEV_FDB_ADD_TO_DEVICE:
180eea65b87SRoger Quadros case SWITCHDEV_FDB_DEL_TO_DEVICE:
181eea65b87SRoger Quadros memcpy(&switchdev_work->fdb_info, ptr,
182eea65b87SRoger Quadros sizeof(switchdev_work->fdb_info));
183eea65b87SRoger Quadros switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
184eea65b87SRoger Quadros if (!switchdev_work->fdb_info.addr)
185eea65b87SRoger Quadros goto err_addr_alloc;
186eea65b87SRoger Quadros ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
187eea65b87SRoger Quadros fdb_info->addr);
188eea65b87SRoger Quadros netdev_hold(ndev, &switchdev_work->ndev_tracker, GFP_ATOMIC);
189eea65b87SRoger Quadros break;
190eea65b87SRoger Quadros default:
191eea65b87SRoger Quadros kfree(switchdev_work);
192eea65b87SRoger Quadros return NOTIFY_DONE;
193eea65b87SRoger Quadros }
194eea65b87SRoger Quadros
195eea65b87SRoger Quadros queue_work(system_long_wq, &switchdev_work->work);
196eea65b87SRoger Quadros
197eea65b87SRoger Quadros return NOTIFY_DONE;
198eea65b87SRoger Quadros
199eea65b87SRoger Quadros err_addr_alloc:
200eea65b87SRoger Quadros kfree(switchdev_work);
201eea65b87SRoger Quadros return NOTIFY_BAD;
202eea65b87SRoger Quadros }
203eea65b87SRoger Quadros
icssm_prueth_switchdev_obj_add(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj,struct netlink_ext_ack * extack)204eea65b87SRoger Quadros static int icssm_prueth_switchdev_obj_add(struct net_device *ndev,
205eea65b87SRoger Quadros const void *ctx,
206eea65b87SRoger Quadros const struct switchdev_obj *obj,
207eea65b87SRoger Quadros struct netlink_ext_ack *extack)
208eea65b87SRoger Quadros {
209eea65b87SRoger Quadros struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
210eea65b87SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev);
211eea65b87SRoger Quadros struct prueth *prueth = emac->prueth;
212eea65b87SRoger Quadros int ret = 0;
213eea65b87SRoger Quadros u8 hash;
214eea65b87SRoger Quadros
215eea65b87SRoger Quadros switch (obj->id) {
216eea65b87SRoger Quadros case SWITCHDEV_OBJ_ID_HOST_MDB:
217eea65b87SRoger Quadros dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n",
218eea65b87SRoger Quadros ndev->name, mdb->vid, mdb->addr, emac->port_id);
219eea65b87SRoger Quadros hash = icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask);
220eea65b87SRoger Quadros icssm_emac_mc_filter_bin_allow(emac, hash);
221eea65b87SRoger Quadros break;
222eea65b87SRoger Quadros default:
223eea65b87SRoger Quadros ret = -EOPNOTSUPP;
224eea65b87SRoger Quadros break;
225eea65b87SRoger Quadros }
226eea65b87SRoger Quadros
227eea65b87SRoger Quadros return ret;
228eea65b87SRoger Quadros }
229eea65b87SRoger Quadros
icssm_prueth_switchdev_obj_del(struct net_device * ndev,const void * ctx,const struct switchdev_obj * obj)230eea65b87SRoger Quadros static int icssm_prueth_switchdev_obj_del(struct net_device *ndev,
231eea65b87SRoger Quadros const void *ctx,
232eea65b87SRoger Quadros const struct switchdev_obj *obj)
233eea65b87SRoger Quadros {
234eea65b87SRoger Quadros struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
235eea65b87SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev);
236eea65b87SRoger Quadros struct prueth *prueth = emac->prueth;
237eea65b87SRoger Quadros struct netdev_hw_addr *ha;
238eea65b87SRoger Quadros u8 hash, tmp_hash;
239eea65b87SRoger Quadros int ret = 0;
240eea65b87SRoger Quadros u8 *mask;
241eea65b87SRoger Quadros
242eea65b87SRoger Quadros switch (obj->id) {
243eea65b87SRoger Quadros case SWITCHDEV_OBJ_ID_HOST_MDB:
244eea65b87SRoger Quadros dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n",
245eea65b87SRoger Quadros ndev->name, mdb->vid, mdb->addr, emac->port_id);
246eea65b87SRoger Quadros if (prueth->hw_bridge_dev) {
247eea65b87SRoger Quadros mask = emac->mc_filter_mask;
248eea65b87SRoger Quadros hash = icssm_emac_get_mc_hash(mdb->addr, mask);
249eea65b87SRoger Quadros netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) {
250eea65b87SRoger Quadros tmp_hash = icssm_emac_get_mc_hash(ha->addr,
251eea65b87SRoger Quadros mask);
252eea65b87SRoger Quadros /* Another MC address is in the bin.
253eea65b87SRoger Quadros * Don't disable.
254eea65b87SRoger Quadros */
255eea65b87SRoger Quadros if (tmp_hash == hash)
256eea65b87SRoger Quadros return 0;
257eea65b87SRoger Quadros }
258eea65b87SRoger Quadros icssm_emac_mc_filter_bin_disallow(emac, hash);
259eea65b87SRoger Quadros }
260eea65b87SRoger Quadros break;
261eea65b87SRoger Quadros default:
262eea65b87SRoger Quadros ret = -EOPNOTSUPP;
263eea65b87SRoger Quadros break;
264eea65b87SRoger Quadros }
265eea65b87SRoger Quadros
266eea65b87SRoger Quadros return ret;
267eea65b87SRoger Quadros }
268eea65b87SRoger Quadros
269eea65b87SRoger Quadros /* switchdev notifiers */
icssm_prueth_sw_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)270eea65b87SRoger Quadros static int icssm_prueth_sw_blocking_event(struct notifier_block *unused,
271eea65b87SRoger Quadros unsigned long event, void *ptr)
272eea65b87SRoger Quadros {
273eea65b87SRoger Quadros struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
274eea65b87SRoger Quadros int err;
275eea65b87SRoger Quadros
276eea65b87SRoger Quadros switch (event) {
277eea65b87SRoger Quadros case SWITCHDEV_PORT_OBJ_ADD:
278eea65b87SRoger Quadros err = switchdev_handle_port_obj_add
279eea65b87SRoger Quadros (ndev, ptr, icssm_prueth_sw_port_dev_check,
280eea65b87SRoger Quadros icssm_prueth_switchdev_obj_add);
281eea65b87SRoger Quadros return notifier_from_errno(err);
282eea65b87SRoger Quadros
283eea65b87SRoger Quadros case SWITCHDEV_PORT_OBJ_DEL:
284eea65b87SRoger Quadros err = switchdev_handle_port_obj_del
285eea65b87SRoger Quadros (ndev, ptr, icssm_prueth_sw_port_dev_check,
286eea65b87SRoger Quadros icssm_prueth_switchdev_obj_del);
287eea65b87SRoger Quadros return notifier_from_errno(err);
288eea65b87SRoger Quadros
289eea65b87SRoger Quadros case SWITCHDEV_PORT_ATTR_SET:
290eea65b87SRoger Quadros err = switchdev_handle_port_attr_set
291eea65b87SRoger Quadros (ndev, ptr, icssm_prueth_sw_port_dev_check,
292eea65b87SRoger Quadros icssm_prueth_sw_attr_set);
293eea65b87SRoger Quadros return notifier_from_errno(err);
294eea65b87SRoger Quadros
295eea65b87SRoger Quadros default:
296eea65b87SRoger Quadros break;
297eea65b87SRoger Quadros }
298eea65b87SRoger Quadros
299eea65b87SRoger Quadros return NOTIFY_DONE;
300eea65b87SRoger Quadros }
301eea65b87SRoger Quadros
icssm_prueth_sw_register_notifiers(struct prueth * prueth)302eea65b87SRoger Quadros int icssm_prueth_sw_register_notifiers(struct prueth *prueth)
303eea65b87SRoger Quadros {
304eea65b87SRoger Quadros int ret = 0;
305eea65b87SRoger Quadros
306eea65b87SRoger Quadros prueth->prueth_switchdev_nb.notifier_call =
307eea65b87SRoger Quadros &icssm_prueth_sw_switchdev_event;
308eea65b87SRoger Quadros ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
309eea65b87SRoger Quadros if (ret) {
310eea65b87SRoger Quadros dev_err(prueth->dev,
311eea65b87SRoger Quadros "register switchdev notifier failed ret:%d\n", ret);
312eea65b87SRoger Quadros return ret;
313eea65b87SRoger Quadros }
314eea65b87SRoger Quadros
315eea65b87SRoger Quadros prueth->prueth_switchdev_bl_nb.notifier_call =
316eea65b87SRoger Quadros &icssm_prueth_sw_blocking_event;
317eea65b87SRoger Quadros ret = register_switchdev_blocking_notifier
318eea65b87SRoger Quadros (&prueth->prueth_switchdev_bl_nb);
319eea65b87SRoger Quadros if (ret) {
320eea65b87SRoger Quadros dev_err(prueth->dev,
321eea65b87SRoger Quadros "register switchdev blocking notifier failed ret:%d\n",
322eea65b87SRoger Quadros ret);
323eea65b87SRoger Quadros unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
324eea65b87SRoger Quadros }
325eea65b87SRoger Quadros
326eea65b87SRoger Quadros return ret;
327eea65b87SRoger Quadros }
328eea65b87SRoger Quadros
icssm_prueth_sw_unregister_notifiers(struct prueth * prueth)329eea65b87SRoger Quadros void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth)
330eea65b87SRoger Quadros {
331eea65b87SRoger Quadros unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
332eea65b87SRoger Quadros unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
333eea65b87SRoger Quadros }
334