#include <net/ipv6.h>
 #include <net/addrconf.h>
 #include <linux/if_ether.h>
+#include <linux/auxiliary_bus.h>
 
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_user_verbs.h>
 MODULE_LICENSE("Dual BSD/GPL");
 
 /* globals */
-static struct list_head bnxt_re_dev_list = LIST_HEAD_INIT(bnxt_re_dev_list);
-/* Mutex to protect the list of bnxt_re devices added */
-static DEFINE_MUTEX(bnxt_re_dev_lock);
-static struct workqueue_struct *bnxt_re_wq;
-static void bnxt_re_remove_device(struct bnxt_re_dev *rdev);
-static void bnxt_re_dealloc_driver(struct ib_device *ib_dev);
+static DEFINE_MUTEX(bnxt_re_mutex);
+
 static void bnxt_re_stop_irq(void *handle);
 static void bnxt_re_dev_stop(struct bnxt_re_dev *rdev);
+static int bnxt_re_netdev_event(struct notifier_block *notifier,
+                               unsigned long event, void *ptr);
+static struct bnxt_re_dev *bnxt_re_from_netdev(struct net_device *netdev);
+static void bnxt_re_dev_uninit(struct bnxt_re_dev *rdev);
 
 static void bnxt_re_set_drv_mode(struct bnxt_re_dev *rdev, u8 mode)
 {
 
        if (!rdev)
                return;
-       ASSERT_RTNL();
 
        /* L2 driver invokes this callback during device error/crash or device
         * reset. Current RoCE driver doesn't recover the device in case of
 {
        struct bnxt_re_dev *rdev = p;
 
-       if (!rdev)
-               return;
-
        if (test_bit(BNXT_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags))
                return;
        rdev->num_vfs = num_vfs;
        }
 }
 
-static void bnxt_re_shutdown(void *p)
+static void bnxt_re_shutdown(struct auxiliary_device *adev)
 {
-       struct bnxt_re_dev *rdev = p;
+       struct bnxt_re_dev *rdev = auxiliary_get_drvdata(adev);
 
        if (!rdev)
                return;
-       ASSERT_RTNL();
-       /* Release the MSIx vectors before queuing unregister */
-       bnxt_re_stop_irq(rdev);
-       ib_unregister_device_queued(&rdev->ibdev);
+       ib_unregister_device(&rdev->ibdev);
+       bnxt_re_dev_uninit(rdev);
 }
 
 static void bnxt_re_stop_irq(void *handle)
 }
 
 static struct bnxt_ulp_ops bnxt_re_ulp_ops = {
-       .ulp_async_notifier = NULL,
        .ulp_stop = bnxt_re_stop,
        .ulp_start = bnxt_re_start,
        .ulp_sriov_config = bnxt_re_sriov_config,
-       .ulp_shutdown = bnxt_re_shutdown,
        .ulp_irq_stop = bnxt_re_stop_irq,
        .ulp_irq_restart = bnxt_re_start_irq
 };
        struct bnxt_en_dev *en_dev;
        int rc = 0;
 
-       if (!rdev)
-               return -EINVAL;
-
        en_dev = rdev->en_dev;
 
        rc = en_dev->en_ops->bnxt_register_device(en_dev, BNXT_ROCE_ULP,
 
        en_dev = rdev->en_dev;
 
-
        rc = en_dev->en_ops->bnxt_free_msix(rdev->en_dev, BNXT_ROCE_ULP);
 
        return rc;
        int rc = 0, num_msix_want = BNXT_RE_MAX_MSIX, num_msix_got;
        struct bnxt_en_dev *en_dev;
 
-       if (!rdev)
-               return -EINVAL;
-
        en_dev = rdev->en_dev;
 
        num_msix_want = min_t(u32, BNXT_RE_MAX_MSIX, num_online_cpus());
 static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev,
                                 u16 fw_ring_id, int type)
 {
-       struct bnxt_en_dev *en_dev = rdev->en_dev;
+       struct bnxt_en_dev *en_dev;
        struct hwrm_ring_free_input req = {0};
        struct hwrm_ring_free_output resp;
        struct bnxt_fw_msg fw_msg;
        int rc = -EINVAL;
 
+       if (!rdev)
+               return rc;
+
+       en_dev = rdev->en_dev;
+
        if (!en_dev)
                return rc;
 
 
 /* Device */
 
-static bool is_bnxt_re_dev(struct net_device *netdev)
-{
-       struct ethtool_drvinfo drvinfo;
-
-       if (netdev->ethtool_ops && netdev->ethtool_ops->get_drvinfo) {
-               memset(&drvinfo, 0, sizeof(drvinfo));
-               netdev->ethtool_ops->get_drvinfo(netdev, &drvinfo);
-
-               if (strcmp(drvinfo.driver, "bnxt_en"))
-                       return false;
-               return true;
-       }
-       return false;
-}
-
 static struct bnxt_re_dev *bnxt_re_from_netdev(struct net_device *netdev)
 {
        struct ib_device *ibdev =
        return container_of(ibdev, struct bnxt_re_dev, ibdev);
 }
 
-static struct bnxt_en_dev *bnxt_re_dev_probe(struct net_device *netdev)
-{
-       struct bnxt_en_dev *en_dev;
-       struct pci_dev *pdev;
-
-       en_dev = bnxt_ulp_probe(netdev);
-       if (IS_ERR(en_dev))
-               return en_dev;
-
-       pdev = en_dev->pdev;
-       if (!pdev)
-               return ERR_PTR(-EINVAL);
-
-       if (!(en_dev->flags & BNXT_EN_FLAG_ROCE_CAP)) {
-               dev_info(&pdev->dev,
-                       "%s: probe error: RoCE is not supported on this device",
-                       ROCE_DRV_MODULE_NAME);
-               return ERR_PTR(-ENODEV);
-       }
-
-       dev_hold(netdev);
-
-       return en_dev;
-}
-
 static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
                           char *buf)
 {
        .create_qp = bnxt_re_create_qp,
        .create_srq = bnxt_re_create_srq,
        .create_user_ah = bnxt_re_create_ah,
-       .dealloc_driver = bnxt_re_dealloc_driver,
        .dealloc_pd = bnxt_re_dealloc_pd,
        .dealloc_ucontext = bnxt_re_dealloc_ucontext,
        .del_gid = bnxt_re_del_gid,
        return ib_register_device(ibdev, "bnxt_re%d", &rdev->en_dev->pdev->dev);
 }
 
-static void bnxt_re_dev_remove(struct bnxt_re_dev *rdev)
-{
-       dev_put(rdev->netdev);
-       rdev->netdev = NULL;
-       mutex_lock(&bnxt_re_dev_lock);
-       list_del_rcu(&rdev->list);
-       mutex_unlock(&bnxt_re_dev_lock);
-
-       synchronize_rcu();
-}
-
-static struct bnxt_re_dev *bnxt_re_dev_add(struct net_device *netdev,
+static struct bnxt_re_dev *bnxt_re_dev_add(struct bnxt_aux_priv *aux_priv,
                                           struct bnxt_en_dev *en_dev)
 {
        struct bnxt_re_dev *rdev;
                return NULL;
        }
        /* Default values */
-       rdev->netdev = netdev;
-       dev_hold(rdev->netdev);
+       rdev->nb.notifier_call = NULL;
+       rdev->netdev = en_dev->net;
        rdev->en_dev = en_dev;
        rdev->id = rdev->en_dev->pdev->devfn;
        INIT_LIST_HEAD(&rdev->qp_list);
        rdev->cosq[0] = 0xFFFF;
        rdev->cosq[1] = 0xFFFF;
 
-       mutex_lock(&bnxt_re_dev_lock);
-       list_add_tail_rcu(&rdev->list, &bnxt_re_dev_list);
-       mutex_unlock(&bnxt_re_dev_lock);
        return rdev;
 }
 
                pr_err("Failed to register with IB: %#x\n", rc);
                return rc;
        }
-       dev_info(rdev_to_dev(rdev), "Device registered successfully");
+       dev_info(rdev_to_dev(rdev), "Device registered with IB successfully");
        ib_get_eth_speed(&rdev->ibdev, 1, &rdev->active_speed,
                         &rdev->active_width);
        set_bit(BNXT_RE_FLAG_ISSUE_ROCE_STATS, &rdev->flags);
        return rc;
 }
 
-static void bnxt_re_dev_unreg(struct bnxt_re_dev *rdev)
-{
-       struct net_device *netdev = rdev->netdev;
-
-       bnxt_re_dev_remove(rdev);
-
-       if (netdev)
-               dev_put(netdev);
-}
-
-static int bnxt_re_dev_reg(struct bnxt_re_dev **rdev, struct net_device *netdev)
+static int bnxt_re_add_device(struct auxiliary_device *adev, u8 wqe_mode)
 {
+       struct bnxt_aux_priv *aux_priv =
+               container_of(adev, struct bnxt_aux_priv, aux_dev);
        struct bnxt_en_dev *en_dev;
+       struct bnxt_re_dev *rdev;
        int rc = 0;
 
-       if (!is_bnxt_re_dev(netdev))
-               return -ENODEV;
+       /* en_dev should never be NULL as long as adev and aux_dev are valid. */
+       en_dev = aux_priv->edev;
 
-       en_dev = bnxt_re_dev_probe(netdev);
-       if (IS_ERR(en_dev)) {
-               if (en_dev != ERR_PTR(-ENODEV))
-                       ibdev_err(&(*rdev)->ibdev, "%s: Failed to probe\n",
-                                 ROCE_DRV_MODULE_NAME);
-               rc = PTR_ERR(en_dev);
-               goto exit;
-       }
-       *rdev = bnxt_re_dev_add(netdev, en_dev);
-       if (!*rdev) {
+       rdev = bnxt_re_dev_add(aux_priv, en_dev);
+       if (!rdev || !rdev_to_dev(rdev)) {
                rc = -ENOMEM;
-               dev_put(netdev);
                goto exit;
        }
-exit:
-       return rc;
-}
-
-static void bnxt_re_remove_device(struct bnxt_re_dev *rdev)
-{
-       bnxt_re_dev_uninit(rdev);
-       pci_dev_put(rdev->en_dev->pdev);
-       bnxt_re_dev_unreg(rdev);
-}
-
-static int bnxt_re_add_device(struct bnxt_re_dev **rdev,
-                             struct net_device *netdev, u8 wqe_mode)
-{
-       int rc;
 
-       rc = bnxt_re_dev_reg(rdev, netdev);
-       if (rc == -ENODEV)
-               return rc;
-       if (rc) {
-               pr_err("Failed to register with the device %s: %#x\n",
-                      netdev->name, rc);
-               return rc;
-       }
+       rc = bnxt_re_dev_init(rdev, wqe_mode);
+       if (rc)
+               goto re_dev_dealloc;
 
-       pci_dev_get((*rdev)->en_dev->pdev);
-       rc = bnxt_re_dev_init(*rdev, wqe_mode);
+       rc = bnxt_re_ib_init(rdev);
        if (rc) {
-               pci_dev_put((*rdev)->en_dev->pdev);
-               bnxt_re_dev_unreg(*rdev);
+               pr_err("Failed to register with IB: %s",
+                       aux_priv->aux_dev.name);
+               goto re_dev_uninit;
        }
+       auxiliary_set_drvdata(adev, rdev);
 
-       return rc;
-}
-
-static void bnxt_re_dealloc_driver(struct ib_device *ib_dev)
-{
-       struct bnxt_re_dev *rdev =
-               container_of(ib_dev, struct bnxt_re_dev, ibdev);
-
-       dev_info(rdev_to_dev(rdev), "Unregistering Device");
-
-       rtnl_lock();
-       bnxt_re_remove_device(rdev);
-       rtnl_unlock();
-}
-
-/* Handle all deferred netevents tasks */
-static void bnxt_re_task(struct work_struct *work)
-{
-       struct bnxt_re_work *re_work;
-       struct bnxt_re_dev *rdev;
-       int rc = 0;
-
-       re_work = container_of(work, struct bnxt_re_work, work);
-       rdev = re_work->rdev;
-
-       if (re_work->event == NETDEV_REGISTER) {
-               rc = bnxt_re_ib_init(rdev);
-               if (rc) {
-                       ibdev_err(&rdev->ibdev,
-                                 "Failed to register with IB: %#x", rc);
-                       rtnl_lock();
-                       bnxt_re_remove_device(rdev);
-                       rtnl_unlock();
-                       goto exit;
-               }
-               goto exit;
-       }
-
-       if (!ib_device_try_get(&rdev->ibdev))
-               goto exit;
+       return 0;
 
-       switch (re_work->event) {
-       case NETDEV_UP:
-               bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1,
-                                      IB_EVENT_PORT_ACTIVE);
-               break;
-       case NETDEV_DOWN:
-               bnxt_re_dev_stop(rdev);
-               break;
-       case NETDEV_CHANGE:
-               if (!netif_carrier_ok(rdev->netdev))
-                       bnxt_re_dev_stop(rdev);
-               else if (netif_carrier_ok(rdev->netdev))
-                       bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1,
-                                              IB_EVENT_PORT_ACTIVE);
-               ib_get_eth_speed(&rdev->ibdev, 1, &rdev->active_speed,
-                                &rdev->active_width);
-               break;
-       default:
-               break;
-       }
-       ib_device_put(&rdev->ibdev);
+re_dev_uninit:
+       bnxt_re_dev_uninit(rdev);
+re_dev_dealloc:
+       ib_dealloc_device(&rdev->ibdev);
 exit:
-       put_device(&rdev->ibdev.dev);
-       kfree(re_work);
+       return rc;
 }
 
 /*
                                unsigned long event, void *ptr)
 {
        struct net_device *real_dev, *netdev = netdev_notifier_info_to_dev(ptr);
-       struct bnxt_re_work *re_work;
        struct bnxt_re_dev *rdev;
-       int rc = 0;
-       bool sch_work = false;
-       bool release = true;
 
        real_dev = rdma_vlan_dev_real_dev(netdev);
        if (!real_dev)
                real_dev = netdev;
 
-       rdev = bnxt_re_from_netdev(real_dev);
-       if (!rdev && event != NETDEV_REGISTER)
-               return NOTIFY_OK;
-
        if (real_dev != netdev)
                goto exit;
 
-       switch (event) {
-       case NETDEV_REGISTER:
-               if (rdev)
-                       break;
-               rc = bnxt_re_add_device(&rdev, real_dev,
-                                       BNXT_QPLIB_WQE_MODE_STATIC);
-               if (!rc)
-                       sch_work = true;
-               release = false;
-               break;
+       rdev = bnxt_re_from_netdev(real_dev);
+       if (!rdev)
+               return NOTIFY_DONE;
 
-       case NETDEV_UNREGISTER:
-               ib_unregister_device_queued(&rdev->ibdev);
-               break;
 
+       switch (event) {
+       case NETDEV_UP:
+       case NETDEV_DOWN:
+       case NETDEV_CHANGE:
+               bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1,
+                                       netif_carrier_ok(real_dev) ?
+                                       IB_EVENT_PORT_ACTIVE :
+                                       IB_EVENT_PORT_ERR);
+               break;
        default:
-               sch_work = true;
                break;
        }
-       if (sch_work) {
-               /* Allocate for the deferred task */
-               re_work = kzalloc(sizeof(*re_work), GFP_KERNEL);
-               if (re_work) {
-                       get_device(&rdev->ibdev.dev);
-                       re_work->rdev = rdev;
-                       re_work->event = event;
-                       re_work->vlan_dev = (real_dev == netdev ?
-                                            NULL : netdev);
-                       INIT_WORK(&re_work->work, bnxt_re_task);
-                       queue_work(bnxt_re_wq, &re_work->work);
-               }
-       }
-
+       ib_device_put(&rdev->ibdev);
 exit:
-       if (rdev && release)
-               ib_device_put(&rdev->ibdev);
        return NOTIFY_DONE;
 }
 
-static struct notifier_block bnxt_re_netdev_notifier = {
-       .notifier_call = bnxt_re_netdev_event
-};
+#define BNXT_ADEV_NAME "bnxt_en"
 
-static int __init bnxt_re_mod_init(void)
+static void bnxt_re_remove(struct auxiliary_device *adev)
 {
-       int rc = 0;
+       struct bnxt_re_dev *rdev = auxiliary_get_drvdata(adev);
 
-       pr_info("%s: %s", ROCE_DRV_MODULE_NAME, version);
+       if (!rdev)
+               return;
 
-       bnxt_re_wq = create_singlethread_workqueue("bnxt_re");
-       if (!bnxt_re_wq)
-               return -ENOMEM;
+       mutex_lock(&bnxt_re_mutex);
+       if (rdev->nb.notifier_call) {
+               unregister_netdevice_notifier(&rdev->nb);
+               rdev->nb.notifier_call = NULL;
+       } else {
+               /* If notifier is null, we should have already done a
+                * clean up before coming here.
+                */
+               goto skip_remove;
+       }
+
+       ib_unregister_device(&rdev->ibdev);
+       ib_dealloc_device(&rdev->ibdev);
+       bnxt_re_dev_uninit(rdev);
+skip_remove:
+       mutex_unlock(&bnxt_re_mutex);
+}
+
+static int bnxt_re_probe(struct auxiliary_device *adev,
+                        const struct auxiliary_device_id *id)
+{
+       struct bnxt_re_dev *rdev;
+       int rc;
+
+       mutex_lock(&bnxt_re_mutex);
+       rc = bnxt_re_add_device(adev, BNXT_QPLIB_WQE_MODE_STATIC);
+       if (rc) {
+               mutex_unlock(&bnxt_re_mutex);
+               return rc;
+       }
 
-       INIT_LIST_HEAD(&bnxt_re_dev_list);
+       rdev = auxiliary_get_drvdata(adev);
 
-       rc = register_netdevice_notifier(&bnxt_re_netdev_notifier);
+       rdev->nb.notifier_call = bnxt_re_netdev_event;
+       rc = register_netdevice_notifier(&rdev->nb);
        if (rc) {
+               rdev->nb.notifier_call = NULL;
                pr_err("%s: Cannot register to netdevice_notifier",
                       ROCE_DRV_MODULE_NAME);
-               goto err_netdev;
+               goto err;
        }
+
+       mutex_unlock(&bnxt_re_mutex);
        return 0;
 
-err_netdev:
-       destroy_workqueue(bnxt_re_wq);
+err:
+       mutex_unlock(&bnxt_re_mutex);
+       bnxt_re_remove(adev);
 
        return rc;
 }
 
-static void __exit bnxt_re_mod_exit(void)
+static const struct auxiliary_device_id bnxt_re_id_table[] = {
+       { .name = BNXT_ADEV_NAME ".rdma", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(auxiliary, bnxt_re_id_table);
+
+static struct auxiliary_driver bnxt_re_driver = {
+       .name = "rdma",
+       .probe = bnxt_re_probe,
+       .remove = bnxt_re_remove,
+       .shutdown = bnxt_re_shutdown,
+       .id_table = bnxt_re_id_table,
+};
+
+static int __init bnxt_re_mod_init(void)
 {
-       struct bnxt_re_dev *rdev;
+       int rc = 0;
 
-       unregister_netdevice_notifier(&bnxt_re_netdev_notifier);
-       if (bnxt_re_wq)
-               destroy_workqueue(bnxt_re_wq);
-       list_for_each_entry(rdev, &bnxt_re_dev_list, list) {
-               /* VF device removal should be called before the removal
-                * of PF device. Queue VFs unregister first, so that VFs
-                * shall be removed before the PF during the call of
-                * ib_unregister_driver.
-                */
-               if (rdev->is_virtfn)
-                       ib_unregister_device(&rdev->ibdev);
+       pr_info("%s: %s", ROCE_DRV_MODULE_NAME, version);
+       rc = auxiliary_driver_register(&bnxt_re_driver);
+       if (rc) {
+               pr_err("%s: Failed to register auxiliary driver\n",
+                       ROCE_DRV_MODULE_NAME);
+               return rc;
        }
-       ib_unregister_driver(RDMA_DRIVER_BNXT_RE);
+       return 0;
+}
+
+static void __exit bnxt_re_mod_exit(void)
+{
+       auxiliary_driver_unregister(&bnxt_re_driver);
 }
 
 module_init(bnxt_re_mod_init);