162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * rionet - Ethernet driver over RapidIO messaging services
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2005 MontaVista Software, Inc.
662306a36Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/rio.h>
1462306a36Sopenharmony_ci#include <linux/rio_drv.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/rio_ids.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/netdevice.h>
1962306a36Sopenharmony_ci#include <linux/etherdevice.h>
2062306a36Sopenharmony_ci#include <linux/skbuff.h>
2162306a36Sopenharmony_ci#include <linux/crc32.h>
2262306a36Sopenharmony_ci#include <linux/ethtool.h>
2362306a36Sopenharmony_ci#include <linux/reboot.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DRV_NAME        "rionet"
2662306a36Sopenharmony_ci#define DRV_VERSION     "0.3"
2762306a36Sopenharmony_ci#define DRV_AUTHOR      "Matt Porter <mporter@kernel.crashing.org>"
2862306a36Sopenharmony_ci#define DRV_DESC        "Ethernet over RapidIO"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR);
3162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
3262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define RIONET_DEFAULT_MSGLEVEL \
3562306a36Sopenharmony_ci			(NETIF_MSG_DRV          | \
3662306a36Sopenharmony_ci			 NETIF_MSG_LINK         | \
3762306a36Sopenharmony_ci			 NETIF_MSG_RX_ERR       | \
3862306a36Sopenharmony_ci			 NETIF_MSG_TX_ERR)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define RIONET_DOORBELL_JOIN	0x1000
4162306a36Sopenharmony_ci#define RIONET_DOORBELL_LEAVE	0x1001
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define RIONET_MAILBOX		0
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define RIONET_TX_RING_SIZE	CONFIG_RIONET_TX_SIZE
4662306a36Sopenharmony_ci#define RIONET_RX_RING_SIZE	CONFIG_RIONET_RX_SIZE
4762306a36Sopenharmony_ci#define RIONET_MAX_NETS		8
4862306a36Sopenharmony_ci#define RIONET_MSG_SIZE         RIO_MAX_MSG_SIZE
4962306a36Sopenharmony_ci#define RIONET_MAX_MTU          (RIONET_MSG_SIZE - ETH_HLEN)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct rionet_private {
5262306a36Sopenharmony_ci	struct rio_mport *mport;
5362306a36Sopenharmony_ci	struct sk_buff *rx_skb[RIONET_RX_RING_SIZE];
5462306a36Sopenharmony_ci	struct sk_buff *tx_skb[RIONET_TX_RING_SIZE];
5562306a36Sopenharmony_ci	int rx_slot;
5662306a36Sopenharmony_ci	int tx_slot;
5762306a36Sopenharmony_ci	int tx_cnt;
5862306a36Sopenharmony_ci	int ack_slot;
5962306a36Sopenharmony_ci	spinlock_t lock;
6062306a36Sopenharmony_ci	spinlock_t tx_lock;
6162306a36Sopenharmony_ci	u32 msg_enable;
6262306a36Sopenharmony_ci	bool open;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct rionet_peer {
6662306a36Sopenharmony_ci	struct list_head node;
6762306a36Sopenharmony_ci	struct rio_dev *rdev;
6862306a36Sopenharmony_ci	struct resource *res;
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct rionet_net {
7262306a36Sopenharmony_ci	struct net_device *ndev;
7362306a36Sopenharmony_ci	struct list_head peers;
7462306a36Sopenharmony_ci	spinlock_t lock;	/* net info access lock */
7562306a36Sopenharmony_ci	struct rio_dev **active;
7662306a36Sopenharmony_ci	int nact;	/* number of active peers */
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct rionet_net nets[RIONET_MAX_NETS];
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define is_rionet_capable(src_ops, dst_ops)			\
8262306a36Sopenharmony_ci			((src_ops & RIO_SRC_OPS_DATA_MSG) &&	\
8362306a36Sopenharmony_ci			 (dst_ops & RIO_DST_OPS_DATA_MSG) &&	\
8462306a36Sopenharmony_ci			 (src_ops & RIO_SRC_OPS_DOORBELL) &&	\
8562306a36Sopenharmony_ci			 (dst_ops & RIO_DST_OPS_DOORBELL))
8662306a36Sopenharmony_ci#define dev_rionet_capable(dev) \
8762306a36Sopenharmony_ci	is_rionet_capable(dev->src_ops, dev->dst_ops)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define RIONET_MAC_MATCH(x)	(!memcmp((x), "\00\01\00\01", 4))
9062306a36Sopenharmony_ci#define RIONET_GET_DESTID(x)	((*((u8 *)x + 4) << 8) | *((u8 *)x + 5))
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int rionet_rx_clean(struct net_device *ndev)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int i;
9562306a36Sopenharmony_ci	int error = 0;
9662306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
9762306a36Sopenharmony_ci	void *data;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	i = rnet->rx_slot;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	do {
10262306a36Sopenharmony_ci		if (!rnet->rx_skb[i])
10362306a36Sopenharmony_ci			continue;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		if (!(data = rio_get_inb_message(rnet->mport, RIONET_MAILBOX)))
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		rnet->rx_skb[i]->data = data;
10962306a36Sopenharmony_ci		skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE);
11062306a36Sopenharmony_ci		rnet->rx_skb[i]->protocol =
11162306a36Sopenharmony_ci		    eth_type_trans(rnet->rx_skb[i], ndev);
11262306a36Sopenharmony_ci		error = __netif_rx(rnet->rx_skb[i]);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (error == NET_RX_DROP) {
11562306a36Sopenharmony_ci			ndev->stats.rx_dropped++;
11662306a36Sopenharmony_ci		} else {
11762306a36Sopenharmony_ci			ndev->stats.rx_packets++;
11862306a36Sopenharmony_ci			ndev->stats.rx_bytes += RIO_MAX_MSG_SIZE;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	} while ((i = (i + 1) % RIONET_RX_RING_SIZE) != rnet->rx_slot);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return i;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void rionet_rx_fill(struct net_device *ndev, int end)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int i;
12962306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	i = rnet->rx_slot;
13262306a36Sopenharmony_ci	do {
13362306a36Sopenharmony_ci		rnet->rx_skb[i] = dev_alloc_skb(RIO_MAX_MSG_SIZE);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		if (!rnet->rx_skb[i])
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		rio_add_inb_buffer(rnet->mport, RIONET_MAILBOX,
13962306a36Sopenharmony_ci				   rnet->rx_skb[i]->data);
14062306a36Sopenharmony_ci	} while ((i = (i + 1) % RIONET_RX_RING_SIZE) != end);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rnet->rx_slot = i;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,
14662306a36Sopenharmony_ci			       struct rio_dev *rdev)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	rio_add_outb_message(rnet->mport, rdev, 0, skb->data, skb->len);
15162306a36Sopenharmony_ci	rnet->tx_skb[rnet->tx_slot] = skb;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ndev->stats.tx_packets++;
15462306a36Sopenharmony_ci	ndev->stats.tx_bytes += skb->len;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (++rnet->tx_cnt == RIONET_TX_RING_SIZE)
15762306a36Sopenharmony_ci		netif_stop_queue(ndev);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	++rnet->tx_slot;
16062306a36Sopenharmony_ci	rnet->tx_slot &= (RIONET_TX_RING_SIZE - 1);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (netif_msg_tx_queued(rnet))
16362306a36Sopenharmony_ci		printk(KERN_INFO "%s: queued skb len %8.8x\n", DRV_NAME,
16462306a36Sopenharmony_ci		       skb->len);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic netdev_tx_t rionet_start_xmit(struct sk_buff *skb,
17062306a36Sopenharmony_ci				     struct net_device *ndev)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	int i;
17362306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
17462306a36Sopenharmony_ci	struct ethhdr *eth = (struct ethhdr *)skb->data;
17562306a36Sopenharmony_ci	u16 destid;
17662306a36Sopenharmony_ci	unsigned long flags;
17762306a36Sopenharmony_ci	int add_num = 1;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	spin_lock_irqsave(&rnet->tx_lock, flags);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (is_multicast_ether_addr(eth->h_dest))
18262306a36Sopenharmony_ci		add_num = nets[rnet->mport->id].nact;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if ((rnet->tx_cnt + add_num) > RIONET_TX_RING_SIZE) {
18562306a36Sopenharmony_ci		netif_stop_queue(ndev);
18662306a36Sopenharmony_ci		spin_unlock_irqrestore(&rnet->tx_lock, flags);
18762306a36Sopenharmony_ci		printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n",
18862306a36Sopenharmony_ci		       ndev->name);
18962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (is_multicast_ether_addr(eth->h_dest)) {
19362306a36Sopenharmony_ci		int count = 0;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size);
19662306a36Sopenharmony_ci				i++)
19762306a36Sopenharmony_ci			if (nets[rnet->mport->id].active[i]) {
19862306a36Sopenharmony_ci				rionet_queue_tx_msg(skb, ndev,
19962306a36Sopenharmony_ci					nets[rnet->mport->id].active[i]);
20062306a36Sopenharmony_ci				if (count)
20162306a36Sopenharmony_ci					refcount_inc(&skb->users);
20262306a36Sopenharmony_ci				count++;
20362306a36Sopenharmony_ci			}
20462306a36Sopenharmony_ci	} else if (RIONET_MAC_MATCH(eth->h_dest)) {
20562306a36Sopenharmony_ci		destid = RIONET_GET_DESTID(eth->h_dest);
20662306a36Sopenharmony_ci		if (nets[rnet->mport->id].active[destid])
20762306a36Sopenharmony_ci			rionet_queue_tx_msg(skb, ndev,
20862306a36Sopenharmony_ci					nets[rnet->mport->id].active[destid]);
20962306a36Sopenharmony_ci		else {
21062306a36Sopenharmony_ci			/*
21162306a36Sopenharmony_ci			 * If the target device was removed from the list of
21262306a36Sopenharmony_ci			 * active peers but we still have TX packets targeting
21362306a36Sopenharmony_ci			 * it just report sending a packet to the target
21462306a36Sopenharmony_ci			 * (without actual packet transfer).
21562306a36Sopenharmony_ci			 */
21662306a36Sopenharmony_ci			ndev->stats.tx_packets++;
21762306a36Sopenharmony_ci			ndev->stats.tx_bytes += skb->len;
21862306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	spin_unlock_irqrestore(&rnet->tx_lock, flags);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return NETDEV_TX_OK;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u16 tid,
22862306a36Sopenharmony_ci			       u16 info)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct net_device *ndev = dev_id;
23162306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
23262306a36Sopenharmony_ci	struct rionet_peer *peer;
23362306a36Sopenharmony_ci	unsigned char netid = rnet->mport->id;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (netif_msg_intr(rnet))
23662306a36Sopenharmony_ci		printk(KERN_INFO "%s: doorbell sid %4.4x tid %4.4x info %4.4x",
23762306a36Sopenharmony_ci		       DRV_NAME, sid, tid, info);
23862306a36Sopenharmony_ci	if (info == RIONET_DOORBELL_JOIN) {
23962306a36Sopenharmony_ci		if (!nets[netid].active[sid]) {
24062306a36Sopenharmony_ci			spin_lock(&nets[netid].lock);
24162306a36Sopenharmony_ci			list_for_each_entry(peer, &nets[netid].peers, node) {
24262306a36Sopenharmony_ci				if (peer->rdev->destid == sid) {
24362306a36Sopenharmony_ci					nets[netid].active[sid] = peer->rdev;
24462306a36Sopenharmony_ci					nets[netid].nact++;
24562306a36Sopenharmony_ci				}
24662306a36Sopenharmony_ci			}
24762306a36Sopenharmony_ci			spin_unlock(&nets[netid].lock);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			rio_mport_send_doorbell(mport, sid,
25062306a36Sopenharmony_ci						RIONET_DOORBELL_JOIN);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci	} else if (info == RIONET_DOORBELL_LEAVE) {
25362306a36Sopenharmony_ci		spin_lock(&nets[netid].lock);
25462306a36Sopenharmony_ci		if (nets[netid].active[sid]) {
25562306a36Sopenharmony_ci			nets[netid].active[sid] = NULL;
25662306a36Sopenharmony_ci			nets[netid].nact--;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		spin_unlock(&nets[netid].lock);
25962306a36Sopenharmony_ci	} else {
26062306a36Sopenharmony_ci		if (netif_msg_intr(rnet))
26162306a36Sopenharmony_ci			printk(KERN_WARNING "%s: unhandled doorbell\n",
26262306a36Sopenharmony_ci			       DRV_NAME);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void rionet_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int n;
26962306a36Sopenharmony_ci	struct net_device *ndev = dev_id;
27062306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (netif_msg_intr(rnet))
27362306a36Sopenharmony_ci		printk(KERN_INFO "%s: inbound message event, mbox %d slot %d\n",
27462306a36Sopenharmony_ci		       DRV_NAME, mbox, slot);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	spin_lock(&rnet->lock);
27762306a36Sopenharmony_ci	if ((n = rionet_rx_clean(ndev)) != rnet->rx_slot)
27862306a36Sopenharmony_ci		rionet_rx_fill(ndev, n);
27962306a36Sopenharmony_ci	spin_unlock(&rnet->lock);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct net_device *ndev = dev_id;
28562306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	spin_lock(&rnet->tx_lock);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (netif_msg_intr(rnet))
29062306a36Sopenharmony_ci		printk(KERN_INFO
29162306a36Sopenharmony_ci		       "%s: outbound message event, mbox %d slot %d\n",
29262306a36Sopenharmony_ci		       DRV_NAME, mbox, slot);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	while (rnet->tx_cnt && (rnet->ack_slot != slot)) {
29562306a36Sopenharmony_ci		/* dma unmap single */
29662306a36Sopenharmony_ci		dev_kfree_skb_irq(rnet->tx_skb[rnet->ack_slot]);
29762306a36Sopenharmony_ci		rnet->tx_skb[rnet->ack_slot] = NULL;
29862306a36Sopenharmony_ci		++rnet->ack_slot;
29962306a36Sopenharmony_ci		rnet->ack_slot &= (RIONET_TX_RING_SIZE - 1);
30062306a36Sopenharmony_ci		rnet->tx_cnt--;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (rnet->tx_cnt < RIONET_TX_RING_SIZE)
30462306a36Sopenharmony_ci		netif_wake_queue(ndev);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	spin_unlock(&rnet->tx_lock);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int rionet_open(struct net_device *ndev)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	int i, rc = 0;
31262306a36Sopenharmony_ci	struct rionet_peer *peer;
31362306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
31462306a36Sopenharmony_ci	unsigned char netid = rnet->mport->id;
31562306a36Sopenharmony_ci	unsigned long flags;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (netif_msg_ifup(rnet))
31862306a36Sopenharmony_ci		printk(KERN_INFO "%s: open\n", DRV_NAME);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if ((rc = rio_request_inb_dbell(rnet->mport,
32162306a36Sopenharmony_ci					(void *)ndev,
32262306a36Sopenharmony_ci					RIONET_DOORBELL_JOIN,
32362306a36Sopenharmony_ci					RIONET_DOORBELL_LEAVE,
32462306a36Sopenharmony_ci					rionet_dbell_event)) < 0)
32562306a36Sopenharmony_ci		goto out;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if ((rc = rio_request_inb_mbox(rnet->mport,
32862306a36Sopenharmony_ci				       (void *)ndev,
32962306a36Sopenharmony_ci				       RIONET_MAILBOX,
33062306a36Sopenharmony_ci				       RIONET_RX_RING_SIZE,
33162306a36Sopenharmony_ci				       rionet_inb_msg_event)) < 0)
33262306a36Sopenharmony_ci		goto out;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if ((rc = rio_request_outb_mbox(rnet->mport,
33562306a36Sopenharmony_ci					(void *)ndev,
33662306a36Sopenharmony_ci					RIONET_MAILBOX,
33762306a36Sopenharmony_ci					RIONET_TX_RING_SIZE,
33862306a36Sopenharmony_ci					rionet_outb_msg_event)) < 0)
33962306a36Sopenharmony_ci		goto out;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* Initialize inbound message ring */
34262306a36Sopenharmony_ci	for (i = 0; i < RIONET_RX_RING_SIZE; i++)
34362306a36Sopenharmony_ci		rnet->rx_skb[i] = NULL;
34462306a36Sopenharmony_ci	rnet->rx_slot = 0;
34562306a36Sopenharmony_ci	rionet_rx_fill(ndev, 0);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	rnet->tx_slot = 0;
34862306a36Sopenharmony_ci	rnet->tx_cnt = 0;
34962306a36Sopenharmony_ci	rnet->ack_slot = 0;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	netif_carrier_on(ndev);
35262306a36Sopenharmony_ci	netif_start_queue(ndev);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
35562306a36Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
35662306a36Sopenharmony_ci		/* Send a join message */
35762306a36Sopenharmony_ci		rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
36062306a36Sopenharmony_ci	rnet->open = true;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci      out:
36362306a36Sopenharmony_ci	return rc;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int rionet_close(struct net_device *ndev)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
36962306a36Sopenharmony_ci	struct rionet_peer *peer;
37062306a36Sopenharmony_ci	unsigned char netid = rnet->mport->id;
37162306a36Sopenharmony_ci	unsigned long flags;
37262306a36Sopenharmony_ci	int i;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (netif_msg_ifup(rnet))
37562306a36Sopenharmony_ci		printk(KERN_INFO "%s: close %s\n", DRV_NAME, ndev->name);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	netif_stop_queue(ndev);
37862306a36Sopenharmony_ci	netif_carrier_off(ndev);
37962306a36Sopenharmony_ci	rnet->open = false;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	for (i = 0; i < RIONET_RX_RING_SIZE; i++)
38262306a36Sopenharmony_ci		kfree_skb(rnet->rx_skb[i]);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
38562306a36Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
38662306a36Sopenharmony_ci		if (nets[netid].active[peer->rdev->destid]) {
38762306a36Sopenharmony_ci			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_LEAVE);
38862306a36Sopenharmony_ci			nets[netid].active[peer->rdev->destid] = NULL;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		if (peer->res)
39162306a36Sopenharmony_ci			rio_release_outb_dbell(peer->rdev, peer->res);
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	rio_release_inb_dbell(rnet->mport, RIONET_DOORBELL_JOIN,
39662306a36Sopenharmony_ci			      RIONET_DOORBELL_LEAVE);
39762306a36Sopenharmony_ci	rio_release_inb_mbox(rnet->mport, RIONET_MAILBOX);
39862306a36Sopenharmony_ci	rio_release_outb_mbox(rnet->mport, RIONET_MAILBOX);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void rionet_remove_dev(struct device *dev, struct subsys_interface *sif)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
40662306a36Sopenharmony_ci	unsigned char netid = rdev->net->hport->id;
40762306a36Sopenharmony_ci	struct rionet_peer *peer;
40862306a36Sopenharmony_ci	int state, found = 0;
40962306a36Sopenharmony_ci	unsigned long flags;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!dev_rionet_capable(rdev))
41262306a36Sopenharmony_ci		return;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	spin_lock_irqsave(&nets[netid].lock, flags);
41562306a36Sopenharmony_ci	list_for_each_entry(peer, &nets[netid].peers, node) {
41662306a36Sopenharmony_ci		if (peer->rdev == rdev) {
41762306a36Sopenharmony_ci			list_del(&peer->node);
41862306a36Sopenharmony_ci			if (nets[netid].active[rdev->destid]) {
41962306a36Sopenharmony_ci				state = atomic_read(&rdev->state);
42062306a36Sopenharmony_ci				if (state != RIO_DEVICE_GONE &&
42162306a36Sopenharmony_ci				    state != RIO_DEVICE_INITIALIZING) {
42262306a36Sopenharmony_ci					rio_send_doorbell(rdev,
42362306a36Sopenharmony_ci							RIONET_DOORBELL_LEAVE);
42462306a36Sopenharmony_ci				}
42562306a36Sopenharmony_ci				nets[netid].active[rdev->destid] = NULL;
42662306a36Sopenharmony_ci				nets[netid].nact--;
42762306a36Sopenharmony_ci			}
42862306a36Sopenharmony_ci			found = 1;
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	spin_unlock_irqrestore(&nets[netid].lock, flags);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (found) {
43562306a36Sopenharmony_ci		if (peer->res)
43662306a36Sopenharmony_ci			rio_release_outb_dbell(rdev, peer->res);
43762306a36Sopenharmony_ci		kfree(peer);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic void rionet_get_drvinfo(struct net_device *ndev,
44262306a36Sopenharmony_ci			       struct ethtool_drvinfo *info)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
44762306a36Sopenharmony_ci	strscpy(info->version, DRV_VERSION, sizeof(info->version));
44862306a36Sopenharmony_ci	strscpy(info->fw_version, "n/a", sizeof(info->fw_version));
44962306a36Sopenharmony_ci	strscpy(info->bus_info, rnet->mport->name, sizeof(info->bus_info));
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic u32 rionet_get_msglevel(struct net_device *ndev)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return rnet->msg_enable;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void rionet_set_msglevel(struct net_device *ndev, u32 value)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct rionet_private *rnet = netdev_priv(ndev);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	rnet->msg_enable = value;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic const struct ethtool_ops rionet_ethtool_ops = {
46762306a36Sopenharmony_ci	.get_drvinfo = rionet_get_drvinfo,
46862306a36Sopenharmony_ci	.get_msglevel = rionet_get_msglevel,
46962306a36Sopenharmony_ci	.set_msglevel = rionet_set_msglevel,
47062306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic const struct net_device_ops rionet_netdev_ops = {
47462306a36Sopenharmony_ci	.ndo_open		= rionet_open,
47562306a36Sopenharmony_ci	.ndo_stop		= rionet_close,
47662306a36Sopenharmony_ci	.ndo_start_xmit		= rionet_start_xmit,
47762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
47862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
47962306a36Sopenharmony_ci};
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	int rc = 0;
48462306a36Sopenharmony_ci	struct rionet_private *rnet;
48562306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
48662306a36Sopenharmony_ci	u16 device_id;
48762306a36Sopenharmony_ci	const size_t rionet_active_bytes = sizeof(void *) *
48862306a36Sopenharmony_ci				RIO_MAX_ROUTE_ENTRIES(mport->sys_size);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	nets[mport->id].active = (struct rio_dev **)__get_free_pages(GFP_KERNEL,
49162306a36Sopenharmony_ci						get_order(rionet_active_bytes));
49262306a36Sopenharmony_ci	if (!nets[mport->id].active) {
49362306a36Sopenharmony_ci		rc = -ENOMEM;
49462306a36Sopenharmony_ci		goto out;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	memset((void *)nets[mport->id].active, 0, rionet_active_bytes);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* Set up private area */
49962306a36Sopenharmony_ci	rnet = netdev_priv(ndev);
50062306a36Sopenharmony_ci	rnet->mport = mport;
50162306a36Sopenharmony_ci	rnet->open = false;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* Set the default MAC address */
50462306a36Sopenharmony_ci	device_id = rio_local_get_device_id(mport);
50562306a36Sopenharmony_ci	addr[0] = 0x00;
50662306a36Sopenharmony_ci	addr[1] = 0x01;
50762306a36Sopenharmony_ci	addr[2] = 0x00;
50862306a36Sopenharmony_ci	addr[3] = 0x01;
50962306a36Sopenharmony_ci	addr[4] = device_id >> 8;
51062306a36Sopenharmony_ci	addr[5] = device_id & 0xff;
51162306a36Sopenharmony_ci	eth_hw_addr_set(ndev, addr);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ndev->netdev_ops = &rionet_netdev_ops;
51462306a36Sopenharmony_ci	ndev->mtu = RIONET_MAX_MTU;
51562306a36Sopenharmony_ci	/* MTU range: 68 - 4082 */
51662306a36Sopenharmony_ci	ndev->min_mtu = ETH_MIN_MTU;
51762306a36Sopenharmony_ci	ndev->max_mtu = RIONET_MAX_MTU;
51862306a36Sopenharmony_ci	ndev->features = NETIF_F_LLTX;
51962306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, &mport->dev);
52062306a36Sopenharmony_ci	ndev->ethtool_ops = &rionet_ethtool_ops;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	spin_lock_init(&rnet->lock);
52362306a36Sopenharmony_ci	spin_lock_init(&rnet->tx_lock);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	rnet->msg_enable = RIONET_DEFAULT_MSGLEVEL;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	rc = register_netdev(ndev);
52862306a36Sopenharmony_ci	if (rc != 0) {
52962306a36Sopenharmony_ci		free_pages((unsigned long)nets[mport->id].active,
53062306a36Sopenharmony_ci			   get_order(rionet_active_bytes));
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	printk(KERN_INFO "%s: %s %s Version %s, MAC %pM, %s\n",
53562306a36Sopenharmony_ci	       ndev->name,
53662306a36Sopenharmony_ci	       DRV_NAME,
53762306a36Sopenharmony_ci	       DRV_DESC,
53862306a36Sopenharmony_ci	       DRV_VERSION,
53962306a36Sopenharmony_ci	       ndev->dev_addr,
54062306a36Sopenharmony_ci	       mport->name);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci      out:
54362306a36Sopenharmony_ci	return rc;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int rionet_add_dev(struct device *dev, struct subsys_interface *sif)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	int rc = -ENODEV;
54962306a36Sopenharmony_ci	u32 lsrc_ops, ldst_ops;
55062306a36Sopenharmony_ci	struct rionet_peer *peer;
55162306a36Sopenharmony_ci	struct net_device *ndev = NULL;
55262306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
55362306a36Sopenharmony_ci	unsigned char netid = rdev->net->hport->id;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (netid >= RIONET_MAX_NETS)
55662306a36Sopenharmony_ci		return rc;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/*
55962306a36Sopenharmony_ci	 * If first time through this net, make sure local device is rionet
56062306a36Sopenharmony_ci	 * capable and setup netdev (this step will be skipped in later probes
56162306a36Sopenharmony_ci	 * on the same net).
56262306a36Sopenharmony_ci	 */
56362306a36Sopenharmony_ci	if (!nets[netid].ndev) {
56462306a36Sopenharmony_ci		rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
56562306a36Sopenharmony_ci					 &lsrc_ops);
56662306a36Sopenharmony_ci		rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,
56762306a36Sopenharmony_ci					 &ldst_ops);
56862306a36Sopenharmony_ci		if (!is_rionet_capable(lsrc_ops, ldst_ops)) {
56962306a36Sopenharmony_ci			printk(KERN_ERR
57062306a36Sopenharmony_ci			       "%s: local device %s is not network capable\n",
57162306a36Sopenharmony_ci			       DRV_NAME, rdev->net->hport->name);
57262306a36Sopenharmony_ci			goto out;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		/* Allocate our net_device structure */
57662306a36Sopenharmony_ci		ndev = alloc_etherdev(sizeof(struct rionet_private));
57762306a36Sopenharmony_ci		if (ndev == NULL) {
57862306a36Sopenharmony_ci			rc = -ENOMEM;
57962306a36Sopenharmony_ci			goto out;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		rc = rionet_setup_netdev(rdev->net->hport, ndev);
58362306a36Sopenharmony_ci		if (rc) {
58462306a36Sopenharmony_ci			printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n",
58562306a36Sopenharmony_ci			       DRV_NAME, rc);
58662306a36Sopenharmony_ci			free_netdev(ndev);
58762306a36Sopenharmony_ci			goto out;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		INIT_LIST_HEAD(&nets[netid].peers);
59162306a36Sopenharmony_ci		spin_lock_init(&nets[netid].lock);
59262306a36Sopenharmony_ci		nets[netid].nact = 0;
59362306a36Sopenharmony_ci		nets[netid].ndev = ndev;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/*
59762306a36Sopenharmony_ci	 * If the remote device has mailbox/doorbell capabilities,
59862306a36Sopenharmony_ci	 * add it to the peer list.
59962306a36Sopenharmony_ci	 */
60062306a36Sopenharmony_ci	if (dev_rionet_capable(rdev)) {
60162306a36Sopenharmony_ci		struct rionet_private *rnet;
60262306a36Sopenharmony_ci		unsigned long flags;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		rnet = netdev_priv(nets[netid].ndev);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		peer = kzalloc(sizeof(*peer), GFP_KERNEL);
60762306a36Sopenharmony_ci		if (!peer) {
60862306a36Sopenharmony_ci			rc = -ENOMEM;
60962306a36Sopenharmony_ci			goto out;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		peer->rdev = rdev;
61262306a36Sopenharmony_ci		peer->res = rio_request_outb_dbell(peer->rdev,
61362306a36Sopenharmony_ci						RIONET_DOORBELL_JOIN,
61462306a36Sopenharmony_ci						RIONET_DOORBELL_LEAVE);
61562306a36Sopenharmony_ci		if (!peer->res) {
61662306a36Sopenharmony_ci			pr_err("%s: error requesting doorbells\n", DRV_NAME);
61762306a36Sopenharmony_ci			kfree(peer);
61862306a36Sopenharmony_ci			rc = -ENOMEM;
61962306a36Sopenharmony_ci			goto out;
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		spin_lock_irqsave(&nets[netid].lock, flags);
62362306a36Sopenharmony_ci		list_add_tail(&peer->node, &nets[netid].peers);
62462306a36Sopenharmony_ci		spin_unlock_irqrestore(&nets[netid].lock, flags);
62562306a36Sopenharmony_ci		pr_debug("%s: %s add peer %s\n",
62662306a36Sopenharmony_ci			 DRV_NAME, __func__, rio_name(rdev));
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		/* If netdev is already opened, send join request to new peer */
62962306a36Sopenharmony_ci		if (rnet->open)
63062306a36Sopenharmony_ci			rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	return 0;
63462306a36Sopenharmony_ciout:
63562306a36Sopenharmony_ci	return rc;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int rionet_shutdown(struct notifier_block *nb, unsigned long code,
63962306a36Sopenharmony_ci			   void *unused)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct rionet_peer *peer;
64262306a36Sopenharmony_ci	unsigned long flags;
64362306a36Sopenharmony_ci	int i;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	pr_debug("%s: %s\n", DRV_NAME, __func__);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	for (i = 0; i < RIONET_MAX_NETS; i++) {
64862306a36Sopenharmony_ci		if (!nets[i].ndev)
64962306a36Sopenharmony_ci			continue;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		spin_lock_irqsave(&nets[i].lock, flags);
65262306a36Sopenharmony_ci		list_for_each_entry(peer, &nets[i].peers, node) {
65362306a36Sopenharmony_ci			if (nets[i].active[peer->rdev->destid]) {
65462306a36Sopenharmony_ci				rio_send_doorbell(peer->rdev,
65562306a36Sopenharmony_ci						  RIONET_DOORBELL_LEAVE);
65662306a36Sopenharmony_ci				nets[i].active[peer->rdev->destid] = NULL;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci		spin_unlock_irqrestore(&nets[i].lock, flags);
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return NOTIFY_DONE;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void rionet_remove_mport(struct device *dev)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct rio_mport *mport = to_rio_mport(dev);
66862306a36Sopenharmony_ci	struct net_device *ndev;
66962306a36Sopenharmony_ci	int id = mport->id;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	pr_debug("%s %s\n", __func__, mport->name);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	WARN(nets[id].nact, "%s called when connected to %d peers\n",
67462306a36Sopenharmony_ci	     __func__, nets[id].nact);
67562306a36Sopenharmony_ci	WARN(!nets[id].ndev, "%s called for mport without NDEV\n",
67662306a36Sopenharmony_ci	     __func__);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (nets[id].ndev) {
67962306a36Sopenharmony_ci		ndev = nets[id].ndev;
68062306a36Sopenharmony_ci		netif_stop_queue(ndev);
68162306a36Sopenharmony_ci		unregister_netdev(ndev);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		free_pages((unsigned long)nets[id].active,
68462306a36Sopenharmony_ci			   get_order(sizeof(void *) *
68562306a36Sopenharmony_ci			   RIO_MAX_ROUTE_ENTRIES(mport->sys_size)));
68662306a36Sopenharmony_ci		nets[id].active = NULL;
68762306a36Sopenharmony_ci		free_netdev(ndev);
68862306a36Sopenharmony_ci		nets[id].ndev = NULL;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci#ifdef MODULE
69362306a36Sopenharmony_cistatic struct rio_device_id rionet_id_table[] = {
69462306a36Sopenharmony_ci	{RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)},
69562306a36Sopenharmony_ci	{ 0, }	/* terminate list */
69662306a36Sopenharmony_ci};
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(rapidio, rionet_id_table);
69962306a36Sopenharmony_ci#endif
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic struct subsys_interface rionet_interface = {
70262306a36Sopenharmony_ci	.name		= "rionet",
70362306a36Sopenharmony_ci	.subsys		= &rio_bus_type,
70462306a36Sopenharmony_ci	.add_dev	= rionet_add_dev,
70562306a36Sopenharmony_ci	.remove_dev	= rionet_remove_dev,
70662306a36Sopenharmony_ci};
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic struct notifier_block rionet_notifier = {
70962306a36Sopenharmony_ci	.notifier_call = rionet_shutdown,
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci/* the rio_mport_interface is used to handle local mport devices */
71362306a36Sopenharmony_cistatic struct class_interface rio_mport_interface __refdata = {
71462306a36Sopenharmony_ci	.class = &rio_mport_class,
71562306a36Sopenharmony_ci	.add_dev = NULL,
71662306a36Sopenharmony_ci	.remove_dev = rionet_remove_mport,
71762306a36Sopenharmony_ci};
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int __init rionet_init(void)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	int ret;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ret = register_reboot_notifier(&rionet_notifier);
72462306a36Sopenharmony_ci	if (ret) {
72562306a36Sopenharmony_ci		pr_err("%s: failed to register reboot notifier (err=%d)\n",
72662306a36Sopenharmony_ci		       DRV_NAME, ret);
72762306a36Sopenharmony_ci		return ret;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	ret = class_interface_register(&rio_mport_interface);
73162306a36Sopenharmony_ci	if (ret) {
73262306a36Sopenharmony_ci		pr_err("%s: class_interface_register error: %d\n",
73362306a36Sopenharmony_ci		       DRV_NAME, ret);
73462306a36Sopenharmony_ci		return ret;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	return subsys_interface_register(&rionet_interface);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void __exit rionet_exit(void)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	unregister_reboot_notifier(&rionet_notifier);
74362306a36Sopenharmony_ci	subsys_interface_unregister(&rionet_interface);
74462306a36Sopenharmony_ci	class_interface_unregister(&rio_mport_interface);
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cilate_initcall(rionet_init);
74862306a36Sopenharmony_cimodule_exit(rionet_exit);
749