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