brintos

brintos / linux-shallow public Read only

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