18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * rionet - Ethernet driver over RapidIO messaging services
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2005 MontaVista Software, Inc.
68c2ecf20Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/rio.h>
148c2ecf20Sopenharmony_ci#include <linux/rio_drv.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/rio_ids.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
218c2ecf20Sopenharmony_ci#include <linux/crc32.h>
228c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
238c2ecf20Sopenharmony_ci#include <linux/reboot.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define DRV_NAME        "rionet"
268c2ecf20Sopenharmony_ci#define DRV_VERSION     "0.3"
278c2ecf20Sopenharmony_ci#define DRV_AUTHOR      "Matt Porter <mporter@kernel.crashing.org>"
288c2ecf20Sopenharmony_ci#define DRV_DESC        "Ethernet over RapidIO"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR);
318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define RIONET_DEFAULT_MSGLEVEL \
358c2ecf20Sopenharmony_ci			(NETIF_MSG_DRV          | \
368c2ecf20Sopenharmony_ci			 NETIF_MSG_LINK         | \
378c2ecf20Sopenharmony_ci			 NETIF_MSG_RX_ERR       | \
388c2ecf20Sopenharmony_ci			 NETIF_MSG_TX_ERR)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define RIONET_DOORBELL_JOIN	0x1000
418c2ecf20Sopenharmony_ci#define RIONET_DOORBELL_LEAVE	0x1001
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define RIONET_MAILBOX		0
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define RIONET_TX_RING_SIZE	CONFIG_RIONET_TX_SIZE
468c2ecf20Sopenharmony_ci#define RIONET_RX_RING_SIZE	CONFIG_RIONET_RX_SIZE
478c2ecf20Sopenharmony_ci#define RIONET_MAX_NETS		8
488c2ecf20Sopenharmony_ci#define RIONET_MSG_SIZE         RIO_MAX_MSG_SIZE
498c2ecf20Sopenharmony_ci#define RIONET_MAX_MTU          (RIONET_MSG_SIZE - ETH_HLEN)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct rionet_private {
528c2ecf20Sopenharmony_ci	struct rio_mport *mport;
538c2ecf20Sopenharmony_ci	struct sk_buff *rx_skb[RIONET_RX_RING_SIZE];
548c2ecf20Sopenharmony_ci	struct sk_buff *tx_skb[RIONET_TX_RING_SIZE];
558c2ecf20Sopenharmony_ci	int rx_slot;
568c2ecf20Sopenharmony_ci	int tx_slot;
578c2ecf20Sopenharmony_ci	int tx_cnt;
588c2ecf20Sopenharmony_ci	int ack_slot;
598c2ecf20Sopenharmony_ci	spinlock_t lock;
608c2ecf20Sopenharmony_ci	spinlock_t tx_lock;
618c2ecf20Sopenharmony_ci	u32 msg_enable;
628c2ecf20Sopenharmony_ci	bool open;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct rionet_peer {
668c2ecf20Sopenharmony_ci	struct list_head node;
678c2ecf20Sopenharmony_ci	struct rio_dev *rdev;
688c2ecf20Sopenharmony_ci	struct resource *res;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct rionet_net {
728c2ecf20Sopenharmony_ci	struct net_device *ndev;
738c2ecf20Sopenharmony_ci	struct list_head peers;
748c2ecf20Sopenharmony_ci	spinlock_t lock;	/* net info access lock */
758c2ecf20Sopenharmony_ci	struct rio_dev **active;
768c2ecf20Sopenharmony_ci	int nact;	/* number of active peers */
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct rionet_net nets[RIONET_MAX_NETS];
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define is_rionet_capable(src_ops, dst_ops)			\
828c2ecf20Sopenharmony_ci			((src_ops & RIO_SRC_OPS_DATA_MSG) &&	\
838c2ecf20Sopenharmony_ci			 (dst_ops & RIO_DST_OPS_DATA_MSG) &&	\
848c2ecf20Sopenharmony_ci			 (src_ops & RIO_SRC_OPS_DOORBELL) &&	\
858c2ecf20Sopenharmony_ci			 (dst_ops & RIO_DST_OPS_DOORBELL))
868c2ecf20Sopenharmony_ci#define dev_rionet_capable(dev) \
878c2ecf20Sopenharmony_ci	is_rionet_capable(dev->src_ops, dev->dst_ops)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define RIONET_MAC_MATCH(x)	(!memcmp((x), "\00\01\00\01", 4))
908c2ecf20Sopenharmony_ci#define RIONET_GET_DESTID(x)	((*((u8 *)x + 4) << 8) | *((u8 *)x + 5))
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int rionet_rx_clean(struct net_device *ndev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int i;
958c2ecf20Sopenharmony_ci	int error = 0;
968c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
978c2ecf20Sopenharmony_ci	void *data;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	i = rnet->rx_slot;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	do {
1028c2ecf20Sopenharmony_ci		if (!rnet->rx_skb[i])
1038c2ecf20Sopenharmony_ci			continue;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		if (!(data = rio_get_inb_message(rnet->mport, RIONET_MAILBOX)))
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		rnet->rx_skb[i]->data = data;
1098c2ecf20Sopenharmony_ci		skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE);
1108c2ecf20Sopenharmony_ci		rnet->rx_skb[i]->protocol =
1118c2ecf20Sopenharmony_ci		    eth_type_trans(rnet->rx_skb[i], ndev);
1128c2ecf20Sopenharmony_ci		error = netif_rx(rnet->rx_skb[i]);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (error == NET_RX_DROP) {
1158c2ecf20Sopenharmony_ci			ndev->stats.rx_dropped++;
1168c2ecf20Sopenharmony_ci		} else {
1178c2ecf20Sopenharmony_ci			ndev->stats.rx_packets++;
1188c2ecf20Sopenharmony_ci			ndev->stats.rx_bytes += RIO_MAX_MSG_SIZE;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	} while ((i = (i + 1) % RIONET_RX_RING_SIZE) != rnet->rx_slot);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return i;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void rionet_rx_fill(struct net_device *ndev, int end)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	int i;
1298c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	i = rnet->rx_slot;
1328c2ecf20Sopenharmony_ci	do {
1338c2ecf20Sopenharmony_ci		rnet->rx_skb[i] = dev_alloc_skb(RIO_MAX_MSG_SIZE);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		if (!rnet->rx_skb[i])
1368c2ecf20Sopenharmony_ci			break;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		rio_add_inb_buffer(rnet->mport, RIONET_MAILBOX,
1398c2ecf20Sopenharmony_ci				   rnet->rx_skb[i]->data);
1408c2ecf20Sopenharmony_ci	} while ((i = (i + 1) % RIONET_RX_RING_SIZE) != end);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	rnet->rx_slot = i;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,
1468c2ecf20Sopenharmony_ci			       struct rio_dev *rdev)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	rio_add_outb_message(rnet->mport, rdev, 0, skb->data, skb->len);
1518c2ecf20Sopenharmony_ci	rnet->tx_skb[rnet->tx_slot] = skb;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ndev->stats.tx_packets++;
1548c2ecf20Sopenharmony_ci	ndev->stats.tx_bytes += skb->len;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (++rnet->tx_cnt == RIONET_TX_RING_SIZE)
1578c2ecf20Sopenharmony_ci		netif_stop_queue(ndev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	++rnet->tx_slot;
1608c2ecf20Sopenharmony_ci	rnet->tx_slot &= (RIONET_TX_RING_SIZE - 1);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (netif_msg_tx_queued(rnet))
1638c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: queued skb len %8.8x\n", DRV_NAME,
1648c2ecf20Sopenharmony_ci		       skb->len);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic netdev_tx_t rionet_start_xmit(struct sk_buff *skb,
1708c2ecf20Sopenharmony_ci				     struct net_device *ndev)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	int i;
1738c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
1748c2ecf20Sopenharmony_ci	struct ethhdr *eth = (struct ethhdr *)skb->data;
1758c2ecf20Sopenharmony_ci	u16 destid;
1768c2ecf20Sopenharmony_ci	unsigned long flags;
1778c2ecf20Sopenharmony_ci	int add_num = 1;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rnet->tx_lock, flags);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(eth->h_dest))
1828c2ecf20Sopenharmony_ci		add_num = nets[rnet->mport->id].nact;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if ((rnet->tx_cnt + add_num) > RIONET_TX_RING_SIZE) {
1858c2ecf20Sopenharmony_ci		netif_stop_queue(ndev);
1868c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&rnet->tx_lock, flags);
1878c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n",
1888c2ecf20Sopenharmony_ci		       ndev->name);
1898c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(eth->h_dest)) {
1938c2ecf20Sopenharmony_ci		int count = 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);
1968c2ecf20Sopenharmony_ci				i++)
1978c2ecf20Sopenharmony_ci			if (nets[rnet->mport->id].active[i]) {
1988c2ecf20Sopenharmony_ci				rionet_queue_tx_msg(skb, ndev,
1998c2ecf20Sopenharmony_ci					nets[rnet->mport->id].active[i]);
2008c2ecf20Sopenharmony_ci				if (count)
2018c2ecf20Sopenharmony_ci					refcount_inc(&skb->users);
2028c2ecf20Sopenharmony_ci				count++;
2038c2ecf20Sopenharmony_ci			}
2048c2ecf20Sopenharmony_ci	} else if (RIONET_MAC_MATCH(eth->h_dest)) {
2058c2ecf20Sopenharmony_ci		destid = RIONET_GET_DESTID(eth->h_dest);
2068c2ecf20Sopenharmony_ci		if (nets[rnet->mport->id].active[destid])
2078c2ecf20Sopenharmony_ci			rionet_queue_tx_msg(skb, ndev,
2088c2ecf20Sopenharmony_ci					nets[rnet->mport->id].active[destid]);
2098c2ecf20Sopenharmony_ci		else {
2108c2ecf20Sopenharmony_ci			/*
2118c2ecf20Sopenharmony_ci			 * If the target device was removed from the list of
2128c2ecf20Sopenharmony_ci			 * active peers but we still have TX packets targeting
2138c2ecf20Sopenharmony_ci			 * it just report sending a packet to the target
2148c2ecf20Sopenharmony_ci			 * (without actual packet transfer).
2158c2ecf20Sopenharmony_ci			 */
2168c2ecf20Sopenharmony_ci			ndev->stats.tx_packets++;
2178c2ecf20Sopenharmony_ci			ndev->stats.tx_bytes += skb->len;
2188c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rnet->tx_lock, flags);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u16 tid,
2288c2ecf20Sopenharmony_ci			       u16 info)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_id;
2318c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
2328c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
2338c2ecf20Sopenharmony_ci	unsigned char netid = rnet->mport->id;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (netif_msg_intr(rnet))
2368c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: doorbell sid %4.4x tid %4.4x info %4.4x",
2378c2ecf20Sopenharmony_ci		       DRV_NAME, sid, tid, info);
2388c2ecf20Sopenharmony_ci	if (info == RIONET_DOORBELL_JOIN) {
2398c2ecf20Sopenharmony_ci		if (!nets[netid].active[sid]) {
2408c2ecf20Sopenharmony_ci			spin_lock(&nets[netid].lock);
2418c2ecf20Sopenharmony_ci			list_for_each_entry(peer, &nets[netid].peers, node) {
2428c2ecf20Sopenharmony_ci				if (peer->rdev->destid == sid) {
2438c2ecf20Sopenharmony_ci					nets[netid].active[sid] = peer->rdev;
2448c2ecf20Sopenharmony_ci					nets[netid].nact++;
2458c2ecf20Sopenharmony_ci				}
2468c2ecf20Sopenharmony_ci			}
2478c2ecf20Sopenharmony_ci			spin_unlock(&nets[netid].lock);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci			rio_mport_send_doorbell(mport, sid,
2508c2ecf20Sopenharmony_ci						RIONET_DOORBELL_JOIN);
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci	} else if (info == RIONET_DOORBELL_LEAVE) {
2538c2ecf20Sopenharmony_ci		spin_lock(&nets[netid].lock);
2548c2ecf20Sopenharmony_ci		if (nets[netid].active[sid]) {
2558c2ecf20Sopenharmony_ci			nets[netid].active[sid] = NULL;
2568c2ecf20Sopenharmony_ci			nets[netid].nact--;
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci		spin_unlock(&nets[netid].lock);
2598c2ecf20Sopenharmony_ci	} else {
2608c2ecf20Sopenharmony_ci		if (netif_msg_intr(rnet))
2618c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: unhandled doorbell\n",
2628c2ecf20Sopenharmony_ci			       DRV_NAME);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void rionet_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	int n;
2698c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_id;
2708c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (netif_msg_intr(rnet))
2738c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: inbound message event, mbox %d slot %d\n",
2748c2ecf20Sopenharmony_ci		       DRV_NAME, mbox, slot);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	spin_lock(&rnet->lock);
2778c2ecf20Sopenharmony_ci	if ((n = rionet_rx_clean(ndev)) != rnet->rx_slot)
2788c2ecf20Sopenharmony_ci		rionet_rx_fill(ndev, n);
2798c2ecf20Sopenharmony_ci	spin_unlock(&rnet->lock);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct net_device *ndev = dev_id;
2858c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	spin_lock(&rnet->tx_lock);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (netif_msg_intr(rnet))
2908c2ecf20Sopenharmony_ci		printk(KERN_INFO
2918c2ecf20Sopenharmony_ci		       "%s: outbound message event, mbox %d slot %d\n",
2928c2ecf20Sopenharmony_ci		       DRV_NAME, mbox, slot);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	while (rnet->tx_cnt && (rnet->ack_slot != slot)) {
2958c2ecf20Sopenharmony_ci		/* dma unmap single */
2968c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(rnet->tx_skb[rnet->ack_slot]);
2978c2ecf20Sopenharmony_ci		rnet->tx_skb[rnet->ack_slot] = NULL;
2988c2ecf20Sopenharmony_ci		++rnet->ack_slot;
2998c2ecf20Sopenharmony_ci		rnet->ack_slot &= (RIONET_TX_RING_SIZE - 1);
3008c2ecf20Sopenharmony_ci		rnet->tx_cnt--;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (rnet->tx_cnt < RIONET_TX_RING_SIZE)
3048c2ecf20Sopenharmony_ci		netif_wake_queue(ndev);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	spin_unlock(&rnet->tx_lock);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int rionet_open(struct net_device *ndev)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int i, rc = 0;
3128c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
3138c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
3148c2ecf20Sopenharmony_ci	unsigned char netid = rnet->mport->id;
3158c2ecf20Sopenharmony_ci	unsigned long flags;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (netif_msg_ifup(rnet))
3188c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: open\n", DRV_NAME);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if ((rc = rio_request_inb_dbell(rnet->mport,
3218c2ecf20Sopenharmony_ci					(void *)ndev,
3228c2ecf20Sopenharmony_ci					RIONET_DOORBELL_JOIN,
3238c2ecf20Sopenharmony_ci					RIONET_DOORBELL_LEAVE,
3248c2ecf20Sopenharmony_ci					rionet_dbell_event)) < 0)
3258c2ecf20Sopenharmony_ci		goto out;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if ((rc = rio_request_inb_mbox(rnet->mport,
3288c2ecf20Sopenharmony_ci				       (void *)ndev,
3298c2ecf20Sopenharmony_ci				       RIONET_MAILBOX,
3308c2ecf20Sopenharmony_ci				       RIONET_RX_RING_SIZE,
3318c2ecf20Sopenharmony_ci				       rionet_inb_msg_event)) < 0)
3328c2ecf20Sopenharmony_ci		goto out;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if ((rc = rio_request_outb_mbox(rnet->mport,
3358c2ecf20Sopenharmony_ci					(void *)ndev,
3368c2ecf20Sopenharmony_ci					RIONET_MAILBOX,
3378c2ecf20Sopenharmony_ci					RIONET_TX_RING_SIZE,
3388c2ecf20Sopenharmony_ci					rionet_outb_msg_event)) < 0)
3398c2ecf20Sopenharmony_ci		goto out;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Initialize inbound message ring */
3428c2ecf20Sopenharmony_ci	for (i = 0; i < RIONET_RX_RING_SIZE; i++)
3438c2ecf20Sopenharmony_ci		rnet->rx_skb[i] = NULL;
3448c2ecf20Sopenharmony_ci	rnet->rx_slot = 0;
3458c2ecf20Sopenharmony_ci	rionet_rx_fill(ndev, 0);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	rnet->tx_slot = 0;
3488c2ecf20Sopenharmony_ci	rnet->tx_cnt = 0;
3498c2ecf20Sopenharmony_ci	rnet->ack_slot = 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	netif_carrier_on(ndev);
3528c2ecf20Sopenharmony_ci	netif_start_queue(ndev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
3558c2ecf20Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
3568c2ecf20Sopenharmony_ci		/* Send a join message */
3578c2ecf20Sopenharmony_ci		rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
3608c2ecf20Sopenharmony_ci	rnet->open = true;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci      out:
3638c2ecf20Sopenharmony_ci	return rc;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int rionet_close(struct net_device *ndev)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
3698c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
3708c2ecf20Sopenharmony_ci	unsigned char netid = rnet->mport->id;
3718c2ecf20Sopenharmony_ci	unsigned long flags;
3728c2ecf20Sopenharmony_ci	int i;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (netif_msg_ifup(rnet))
3758c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: close %s\n", DRV_NAME, ndev->name);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	netif_stop_queue(ndev);
3788c2ecf20Sopenharmony_ci	netif_carrier_off(ndev);
3798c2ecf20Sopenharmony_ci	rnet->open = false;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < RIONET_RX_RING_SIZE; i++)
3828c2ecf20Sopenharmony_ci		kfree_skb(rnet->rx_skb[i]);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
3858c2ecf20Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
3868c2ecf20Sopenharmony_ci		if (nets[netid].active[peer->rdev->destid]) {
3878c2ecf20Sopenharmony_ci			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_LEAVE);
3888c2ecf20Sopenharmony_ci			nets[netid].active[peer->rdev->destid] = NULL;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci		if (peer->res)
3918c2ecf20Sopenharmony_ci			rio_release_outb_dbell(peer->rdev, peer->res);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	rio_release_inb_dbell(rnet->mport, RIONET_DOORBELL_JOIN,
3968c2ecf20Sopenharmony_ci			      RIONET_DOORBELL_LEAVE);
3978c2ecf20Sopenharmony_ci	rio_release_inb_mbox(rnet->mport, RIONET_MAILBOX);
3988c2ecf20Sopenharmony_ci	rio_release_outb_mbox(rnet->mport, RIONET_MAILBOX);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void rionet_remove_dev(struct device *dev, struct subsys_interface *sif)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
4068c2ecf20Sopenharmony_ci	unsigned char netid = rdev->net->hport->id;
4078c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
4088c2ecf20Sopenharmony_ci	int state, found = 0;
4098c2ecf20Sopenharmony_ci	unsigned long flags;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (!dev_rionet_capable(rdev))
4128c2ecf20Sopenharmony_ci		return;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
4158c2ecf20Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
4168c2ecf20Sopenharmony_ci		if (peer->rdev == rdev) {
4178c2ecf20Sopenharmony_ci			list_del(&peer->node);
4188c2ecf20Sopenharmony_ci			if (nets[netid].active[rdev->destid]) {
4198c2ecf20Sopenharmony_ci				state = atomic_read(&rdev->state);
4208c2ecf20Sopenharmony_ci				if (state != RIO_DEVICE_GONE &&
4218c2ecf20Sopenharmony_ci				    state != RIO_DEVICE_INITIALIZING) {
4228c2ecf20Sopenharmony_ci					rio_send_doorbell(rdev,
4238c2ecf20Sopenharmony_ci							RIONET_DOORBELL_LEAVE);
4248c2ecf20Sopenharmony_ci				}
4258c2ecf20Sopenharmony_ci				nets[netid].active[rdev->destid] = NULL;
4268c2ecf20Sopenharmony_ci				nets[netid].nact--;
4278c2ecf20Sopenharmony_ci			}
4288c2ecf20Sopenharmony_ci			found = 1;
4298c2ecf20Sopenharmony_ci			break;
4308c2ecf20Sopenharmony_ci		}
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (found) {
4358c2ecf20Sopenharmony_ci		if (peer->res)
4368c2ecf20Sopenharmony_ci			rio_release_outb_dbell(rdev, peer->res);
4378c2ecf20Sopenharmony_ci		kfree(peer);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic void rionet_get_drvinfo(struct net_device *ndev,
4428c2ecf20Sopenharmony_ci			       struct ethtool_drvinfo *info)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
4478c2ecf20Sopenharmony_ci	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
4488c2ecf20Sopenharmony_ci	strlcpy(info->fw_version, "n/a", sizeof(info->fw_version));
4498c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, rnet->mport->name, sizeof(info->bus_info));
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic u32 rionet_get_msglevel(struct net_device *ndev)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return rnet->msg_enable;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void rionet_set_msglevel(struct net_device *ndev, u32 value)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	rnet->msg_enable = value;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic const struct ethtool_ops rionet_ethtool_ops = {
4678c2ecf20Sopenharmony_ci	.get_drvinfo = rionet_get_drvinfo,
4688c2ecf20Sopenharmony_ci	.get_msglevel = rionet_get_msglevel,
4698c2ecf20Sopenharmony_ci	.set_msglevel = rionet_set_msglevel,
4708c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic const struct net_device_ops rionet_netdev_ops = {
4748c2ecf20Sopenharmony_ci	.ndo_open		= rionet_open,
4758c2ecf20Sopenharmony_ci	.ndo_stop		= rionet_close,
4768c2ecf20Sopenharmony_ci	.ndo_start_xmit		= rionet_start_xmit,
4778c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
4788c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
4798c2ecf20Sopenharmony_ci};
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	int rc = 0;
4848c2ecf20Sopenharmony_ci	struct rionet_private *rnet;
4858c2ecf20Sopenharmony_ci	u16 device_id;
4868c2ecf20Sopenharmony_ci	const size_t rionet_active_bytes = sizeof(void *) *
4878c2ecf20Sopenharmony_ci				RIO_MAX_ROUTE_ENTRIES(mport->sys_size);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	nets[mport->id].active = (struct rio_dev **)__get_free_pages(GFP_KERNEL,
4908c2ecf20Sopenharmony_ci						get_order(rionet_active_bytes));
4918c2ecf20Sopenharmony_ci	if (!nets[mport->id].active) {
4928c2ecf20Sopenharmony_ci		rc = -ENOMEM;
4938c2ecf20Sopenharmony_ci		goto out;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	memset((void *)nets[mport->id].active, 0, rionet_active_bytes);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* Set up private area */
4988c2ecf20Sopenharmony_ci	rnet = netdev_priv(ndev);
4998c2ecf20Sopenharmony_ci	rnet->mport = mport;
5008c2ecf20Sopenharmony_ci	rnet->open = false;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* Set the default MAC address */
5038c2ecf20Sopenharmony_ci	device_id = rio_local_get_device_id(mport);
5048c2ecf20Sopenharmony_ci	ndev->dev_addr[0] = 0x00;
5058c2ecf20Sopenharmony_ci	ndev->dev_addr[1] = 0x01;
5068c2ecf20Sopenharmony_ci	ndev->dev_addr[2] = 0x00;
5078c2ecf20Sopenharmony_ci	ndev->dev_addr[3] = 0x01;
5088c2ecf20Sopenharmony_ci	ndev->dev_addr[4] = device_id >> 8;
5098c2ecf20Sopenharmony_ci	ndev->dev_addr[5] = device_id & 0xff;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	ndev->netdev_ops = &rionet_netdev_ops;
5128c2ecf20Sopenharmony_ci	ndev->mtu = RIONET_MAX_MTU;
5138c2ecf20Sopenharmony_ci	/* MTU range: 68 - 4082 */
5148c2ecf20Sopenharmony_ci	ndev->min_mtu = ETH_MIN_MTU;
5158c2ecf20Sopenharmony_ci	ndev->max_mtu = RIONET_MAX_MTU;
5168c2ecf20Sopenharmony_ci	ndev->features = NETIF_F_LLTX;
5178c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(ndev, &mport->dev);
5188c2ecf20Sopenharmony_ci	ndev->ethtool_ops = &rionet_ethtool_ops;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	spin_lock_init(&rnet->lock);
5218c2ecf20Sopenharmony_ci	spin_lock_init(&rnet->tx_lock);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	rnet->msg_enable = RIONET_DEFAULT_MSGLEVEL;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	rc = register_netdev(ndev);
5268c2ecf20Sopenharmony_ci	if (rc != 0) {
5278c2ecf20Sopenharmony_ci		free_pages((unsigned long)nets[mport->id].active,
5288c2ecf20Sopenharmony_ci			   get_order(rionet_active_bytes));
5298c2ecf20Sopenharmony_ci		goto out;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: %s %s Version %s, MAC %pM, %s\n",
5338c2ecf20Sopenharmony_ci	       ndev->name,
5348c2ecf20Sopenharmony_ci	       DRV_NAME,
5358c2ecf20Sopenharmony_ci	       DRV_DESC,
5368c2ecf20Sopenharmony_ci	       DRV_VERSION,
5378c2ecf20Sopenharmony_ci	       ndev->dev_addr,
5388c2ecf20Sopenharmony_ci	       mport->name);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci      out:
5418c2ecf20Sopenharmony_ci	return rc;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int rionet_add_dev(struct device *dev, struct subsys_interface *sif)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	int rc = -ENODEV;
5478c2ecf20Sopenharmony_ci	u32 lsrc_ops, ldst_ops;
5488c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
5498c2ecf20Sopenharmony_ci	struct net_device *ndev = NULL;
5508c2ecf20Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
5518c2ecf20Sopenharmony_ci	unsigned char netid = rdev->net->hport->id;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (netid >= RIONET_MAX_NETS)
5548c2ecf20Sopenharmony_ci		return rc;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/*
5578c2ecf20Sopenharmony_ci	 * If first time through this net, make sure local device is rionet
5588c2ecf20Sopenharmony_ci	 * capable and setup netdev (this step will be skipped in later probes
5598c2ecf20Sopenharmony_ci	 * on the same net).
5608c2ecf20Sopenharmony_ci	 */
5618c2ecf20Sopenharmony_ci	if (!nets[netid].ndev) {
5628c2ecf20Sopenharmony_ci		rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
5638c2ecf20Sopenharmony_ci					 &lsrc_ops);
5648c2ecf20Sopenharmony_ci		rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,
5658c2ecf20Sopenharmony_ci					 &ldst_ops);
5668c2ecf20Sopenharmony_ci		if (!is_rionet_capable(lsrc_ops, ldst_ops)) {
5678c2ecf20Sopenharmony_ci			printk(KERN_ERR
5688c2ecf20Sopenharmony_ci			       "%s: local device %s is not network capable\n",
5698c2ecf20Sopenharmony_ci			       DRV_NAME, rdev->net->hport->name);
5708c2ecf20Sopenharmony_ci			goto out;
5718c2ecf20Sopenharmony_ci		}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		/* Allocate our net_device structure */
5748c2ecf20Sopenharmony_ci		ndev = alloc_etherdev(sizeof(struct rionet_private));
5758c2ecf20Sopenharmony_ci		if (ndev == NULL) {
5768c2ecf20Sopenharmony_ci			rc = -ENOMEM;
5778c2ecf20Sopenharmony_ci			goto out;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		rc = rionet_setup_netdev(rdev->net->hport, ndev);
5818c2ecf20Sopenharmony_ci		if (rc) {
5828c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n",
5838c2ecf20Sopenharmony_ci			       DRV_NAME, rc);
5848c2ecf20Sopenharmony_ci			free_netdev(ndev);
5858c2ecf20Sopenharmony_ci			goto out;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&nets[netid].peers);
5898c2ecf20Sopenharmony_ci		spin_lock_init(&nets[netid].lock);
5908c2ecf20Sopenharmony_ci		nets[netid].nact = 0;
5918c2ecf20Sopenharmony_ci		nets[netid].ndev = ndev;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	/*
5958c2ecf20Sopenharmony_ci	 * If the remote device has mailbox/doorbell capabilities,
5968c2ecf20Sopenharmony_ci	 * add it to the peer list.
5978c2ecf20Sopenharmony_ci	 */
5988c2ecf20Sopenharmony_ci	if (dev_rionet_capable(rdev)) {
5998c2ecf20Sopenharmony_ci		struct rionet_private *rnet;
6008c2ecf20Sopenharmony_ci		unsigned long flags;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		rnet = netdev_priv(nets[netid].ndev);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		peer = kzalloc(sizeof(*peer), GFP_KERNEL);
6058c2ecf20Sopenharmony_ci		if (!peer) {
6068c2ecf20Sopenharmony_ci			rc = -ENOMEM;
6078c2ecf20Sopenharmony_ci			goto out;
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci		peer->rdev = rdev;
6108c2ecf20Sopenharmony_ci		peer->res = rio_request_outb_dbell(peer->rdev,
6118c2ecf20Sopenharmony_ci						RIONET_DOORBELL_JOIN,
6128c2ecf20Sopenharmony_ci						RIONET_DOORBELL_LEAVE);
6138c2ecf20Sopenharmony_ci		if (!peer->res) {
6148c2ecf20Sopenharmony_ci			pr_err("%s: error requesting doorbells\n", DRV_NAME);
6158c2ecf20Sopenharmony_ci			kfree(peer);
6168c2ecf20Sopenharmony_ci			rc = -ENOMEM;
6178c2ecf20Sopenharmony_ci			goto out;
6188c2ecf20Sopenharmony_ci		}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nets[netid].lock, flags);
6218c2ecf20Sopenharmony_ci		list_add_tail(&peer->node, &nets[netid].peers);
6228c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nets[netid].lock, flags);
6238c2ecf20Sopenharmony_ci		pr_debug("%s: %s add peer %s\n",
6248c2ecf20Sopenharmony_ci			 DRV_NAME, __func__, rio_name(rdev));
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		/* If netdev is already opened, send join request to new peer */
6278c2ecf20Sopenharmony_ci		if (rnet->open)
6288c2ecf20Sopenharmony_ci			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return 0;
6328c2ecf20Sopenharmony_ciout:
6338c2ecf20Sopenharmony_ci	return rc;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int rionet_shutdown(struct notifier_block *nb, unsigned long code,
6378c2ecf20Sopenharmony_ci			   void *unused)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	struct rionet_peer *peer;
6408c2ecf20Sopenharmony_ci	unsigned long flags;
6418c2ecf20Sopenharmony_ci	int i;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	pr_debug("%s: %s\n", DRV_NAME, __func__);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	for (i = 0; i < RIONET_MAX_NETS; i++) {
6468c2ecf20Sopenharmony_ci		if (!nets[i].ndev)
6478c2ecf20Sopenharmony_ci			continue;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nets[i].lock, flags);
6508c2ecf20Sopenharmony_ci		list_for_each_entry(peer, &nets[i].peers, node) {
6518c2ecf20Sopenharmony_ci			if (nets[i].active[peer->rdev->destid]) {
6528c2ecf20Sopenharmony_ci				rio_send_doorbell(peer->rdev,
6538c2ecf20Sopenharmony_ci						  RIONET_DOORBELL_LEAVE);
6548c2ecf20Sopenharmony_ci				nets[i].active[peer->rdev->destid] = NULL;
6558c2ecf20Sopenharmony_ci			}
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nets[i].lock, flags);
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic void rionet_remove_mport(struct device *dev,
6648c2ecf20Sopenharmony_ci				struct class_interface *class_intf)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct rio_mport *mport = to_rio_mport(dev);
6678c2ecf20Sopenharmony_ci	struct net_device *ndev;
6688c2ecf20Sopenharmony_ci	int id = mport->id;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	pr_debug("%s %s\n", __func__, mport->name);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	WARN(nets[id].nact, "%s called when connected to %d peers\n",
6738c2ecf20Sopenharmony_ci	     __func__, nets[id].nact);
6748c2ecf20Sopenharmony_ci	WARN(!nets[id].ndev, "%s called for mport without NDEV\n",
6758c2ecf20Sopenharmony_ci	     __func__);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (nets[id].ndev) {
6788c2ecf20Sopenharmony_ci		ndev = nets[id].ndev;
6798c2ecf20Sopenharmony_ci		netif_stop_queue(ndev);
6808c2ecf20Sopenharmony_ci		unregister_netdev(ndev);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		free_pages((unsigned long)nets[id].active,
6838c2ecf20Sopenharmony_ci			   get_order(sizeof(void *) *
6848c2ecf20Sopenharmony_ci			   RIO_MAX_ROUTE_ENTRIES(mport->sys_size)));
6858c2ecf20Sopenharmony_ci		nets[id].active = NULL;
6868c2ecf20Sopenharmony_ci		free_netdev(ndev);
6878c2ecf20Sopenharmony_ci		nets[id].ndev = NULL;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci#ifdef MODULE
6928c2ecf20Sopenharmony_cistatic struct rio_device_id rionet_id_table[] = {
6938c2ecf20Sopenharmony_ci	{RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)},
6948c2ecf20Sopenharmony_ci	{ 0, }	/* terminate list */
6958c2ecf20Sopenharmony_ci};
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(rapidio, rionet_id_table);
6988c2ecf20Sopenharmony_ci#endif
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic struct subsys_interface rionet_interface = {
7018c2ecf20Sopenharmony_ci	.name		= "rionet",
7028c2ecf20Sopenharmony_ci	.subsys		= &rio_bus_type,
7038c2ecf20Sopenharmony_ci	.add_dev	= rionet_add_dev,
7048c2ecf20Sopenharmony_ci	.remove_dev	= rionet_remove_dev,
7058c2ecf20Sopenharmony_ci};
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic struct notifier_block rionet_notifier = {
7088c2ecf20Sopenharmony_ci	.notifier_call = rionet_shutdown,
7098c2ecf20Sopenharmony_ci};
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci/* the rio_mport_interface is used to handle local mport devices */
7128c2ecf20Sopenharmony_cistatic struct class_interface rio_mport_interface __refdata = {
7138c2ecf20Sopenharmony_ci	.class = &rio_mport_class,
7148c2ecf20Sopenharmony_ci	.add_dev = NULL,
7158c2ecf20Sopenharmony_ci	.remove_dev = rionet_remove_mport,
7168c2ecf20Sopenharmony_ci};
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic int __init rionet_init(void)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	int ret;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ret = register_reboot_notifier(&rionet_notifier);
7238c2ecf20Sopenharmony_ci	if (ret) {
7248c2ecf20Sopenharmony_ci		pr_err("%s: failed to register reboot notifier (err=%d)\n",
7258c2ecf20Sopenharmony_ci		       DRV_NAME, ret);
7268c2ecf20Sopenharmony_ci		return ret;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	ret = class_interface_register(&rio_mport_interface);
7308c2ecf20Sopenharmony_ci	if (ret) {
7318c2ecf20Sopenharmony_ci		pr_err("%s: class_interface_register error: %d\n",
7328c2ecf20Sopenharmony_ci		       DRV_NAME, ret);
7338c2ecf20Sopenharmony_ci		return ret;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	return subsys_interface_register(&rionet_interface);
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic void __exit rionet_exit(void)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&rionet_notifier);
7428c2ecf20Sopenharmony_ci	subsys_interface_unregister(&rionet_interface);
7438c2ecf20Sopenharmony_ci	class_interface_unregister(&rio_mport_interface);
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cilate_initcall(rionet_init);
7478c2ecf20Sopenharmony_cimodule_exit(rionet_exit);
748