162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2012-2016 VMware, Inc.  All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
562306a36Sopenharmony_ci * modify it under the terms of EITHER the GNU General Public License
662306a36Sopenharmony_ci * version 2 as published by the Free Software Foundation or the BSD
762306a36Sopenharmony_ci * 2-Clause License. This program is distributed in the hope that it
862306a36Sopenharmony_ci * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED
962306a36Sopenharmony_ci * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
1062306a36Sopenharmony_ci * See the GNU General Public License version 2 for more details at
1162306a36Sopenharmony_ci * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
1462306a36Sopenharmony_ci * along with this program available in the file COPYING in the main
1562306a36Sopenharmony_ci * directory of this source tree.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * The BSD 2-Clause License
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
2062306a36Sopenharmony_ci *     without modification, are permitted provided that the following
2162306a36Sopenharmony_ci *     conditions are met:
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
2462306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2562306a36Sopenharmony_ci *        disclaimer.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2862306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2962306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
3062306a36Sopenharmony_ci *        provided with the distribution.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3362306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3462306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
3562306a36Sopenharmony_ci * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
3662306a36Sopenharmony_ci * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
3762306a36Sopenharmony_ci * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3862306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3962306a36Sopenharmony_ci * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4062306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4162306a36Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4262306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
4362306a36Sopenharmony_ci * OF THE POSSIBILITY OF SUCH DAMAGE.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/errno.h>
4762306a36Sopenharmony_ci#include <linux/inetdevice.h>
4862306a36Sopenharmony_ci#include <linux/init.h>
4962306a36Sopenharmony_ci#include <linux/module.h>
5062306a36Sopenharmony_ci#include <linux/slab.h>
5162306a36Sopenharmony_ci#include <rdma/ib_addr.h>
5262306a36Sopenharmony_ci#include <rdma/ib_smi.h>
5362306a36Sopenharmony_ci#include <rdma/ib_user_verbs.h>
5462306a36Sopenharmony_ci#include <net/addrconf.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include "pvrdma.h"
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define DRV_NAME	"vmw_pvrdma"
5962306a36Sopenharmony_ci#define DRV_VERSION	"1.0.1.0-k"
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic DEFINE_MUTEX(pvrdma_device_list_lock);
6262306a36Sopenharmony_cistatic LIST_HEAD(pvrdma_device_list);
6362306a36Sopenharmony_cistatic struct workqueue_struct *event_wq;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int pvrdma_add_gid(const struct ib_gid_attr *attr, void **context);
6662306a36Sopenharmony_cistatic int pvrdma_del_gid(const struct ib_gid_attr *attr, void **context);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic ssize_t hca_type_show(struct device *device,
6962306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return sysfs_emit(buf, "VMW_PVRDMA-%s\n", DRV_VERSION);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(hca_type);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic ssize_t hw_rev_show(struct device *device,
7662306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", PVRDMA_REV_ID);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(hw_rev);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic ssize_t board_id_show(struct device *device,
8362306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", PVRDMA_BOARD_ID);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(board_id);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic struct attribute *pvrdma_class_attributes[] = {
9062306a36Sopenharmony_ci	&dev_attr_hw_rev.attr,
9162306a36Sopenharmony_ci	&dev_attr_hca_type.attr,
9262306a36Sopenharmony_ci	&dev_attr_board_id.attr,
9362306a36Sopenharmony_ci	NULL,
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct attribute_group pvrdma_attr_group = {
9762306a36Sopenharmony_ci	.attrs = pvrdma_class_attributes,
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void pvrdma_get_fw_ver_str(struct ib_device *device, char *str)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct pvrdma_dev *dev =
10362306a36Sopenharmony_ci		container_of(device, struct pvrdma_dev, ib_dev);
10462306a36Sopenharmony_ci	snprintf(str, IB_FW_VERSION_NAME_MAX, "%d.%d.%d\n",
10562306a36Sopenharmony_ci		 (int) (dev->dsr->caps.fw_ver >> 32),
10662306a36Sopenharmony_ci		 (int) (dev->dsr->caps.fw_ver >> 16) & 0xffff,
10762306a36Sopenharmony_ci		 (int) dev->dsr->caps.fw_ver & 0xffff);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int pvrdma_init_device(struct pvrdma_dev *dev)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	/*  Initialize some device related stuff */
11362306a36Sopenharmony_ci	spin_lock_init(&dev->cmd_lock);
11462306a36Sopenharmony_ci	sema_init(&dev->cmd_sema, 1);
11562306a36Sopenharmony_ci	atomic_set(&dev->num_qps, 0);
11662306a36Sopenharmony_ci	atomic_set(&dev->num_srqs, 0);
11762306a36Sopenharmony_ci	atomic_set(&dev->num_cqs, 0);
11862306a36Sopenharmony_ci	atomic_set(&dev->num_pds, 0);
11962306a36Sopenharmony_ci	atomic_set(&dev->num_ahs, 0);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int pvrdma_port_immutable(struct ib_device *ibdev, u32 port_num,
12562306a36Sopenharmony_ci				 struct ib_port_immutable *immutable)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(ibdev);
12862306a36Sopenharmony_ci	struct ib_port_attr attr;
12962306a36Sopenharmony_ci	int err;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V1)
13262306a36Sopenharmony_ci		immutable->core_cap_flags |= RDMA_CORE_PORT_IBA_ROCE;
13362306a36Sopenharmony_ci	else if (dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V2)
13462306a36Sopenharmony_ci		immutable->core_cap_flags |= RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	err = ib_query_port(ibdev, port_num, &attr);
13762306a36Sopenharmony_ci	if (err)
13862306a36Sopenharmony_ci		return err;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	immutable->pkey_tbl_len = attr.pkey_tbl_len;
14162306a36Sopenharmony_ci	immutable->gid_tbl_len = attr.gid_tbl_len;
14262306a36Sopenharmony_ci	immutable->max_mad_size = IB_MGMT_MAD_SIZE;
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic const struct ib_device_ops pvrdma_dev_ops = {
14762306a36Sopenharmony_ci	.owner = THIS_MODULE,
14862306a36Sopenharmony_ci	.driver_id = RDMA_DRIVER_VMW_PVRDMA,
14962306a36Sopenharmony_ci	.uverbs_abi_ver = PVRDMA_UVERBS_ABI_VERSION,
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	.add_gid = pvrdma_add_gid,
15262306a36Sopenharmony_ci	.alloc_mr = pvrdma_alloc_mr,
15362306a36Sopenharmony_ci	.alloc_pd = pvrdma_alloc_pd,
15462306a36Sopenharmony_ci	.alloc_ucontext = pvrdma_alloc_ucontext,
15562306a36Sopenharmony_ci	.create_ah = pvrdma_create_ah,
15662306a36Sopenharmony_ci	.create_cq = pvrdma_create_cq,
15762306a36Sopenharmony_ci	.create_qp = pvrdma_create_qp,
15862306a36Sopenharmony_ci	.dealloc_pd = pvrdma_dealloc_pd,
15962306a36Sopenharmony_ci	.dealloc_ucontext = pvrdma_dealloc_ucontext,
16062306a36Sopenharmony_ci	.del_gid = pvrdma_del_gid,
16162306a36Sopenharmony_ci	.dereg_mr = pvrdma_dereg_mr,
16262306a36Sopenharmony_ci	.destroy_ah = pvrdma_destroy_ah,
16362306a36Sopenharmony_ci	.destroy_cq = pvrdma_destroy_cq,
16462306a36Sopenharmony_ci	.destroy_qp = pvrdma_destroy_qp,
16562306a36Sopenharmony_ci	.device_group = &pvrdma_attr_group,
16662306a36Sopenharmony_ci	.get_dev_fw_str = pvrdma_get_fw_ver_str,
16762306a36Sopenharmony_ci	.get_dma_mr = pvrdma_get_dma_mr,
16862306a36Sopenharmony_ci	.get_link_layer = pvrdma_port_link_layer,
16962306a36Sopenharmony_ci	.get_port_immutable = pvrdma_port_immutable,
17062306a36Sopenharmony_ci	.map_mr_sg = pvrdma_map_mr_sg,
17162306a36Sopenharmony_ci	.mmap = pvrdma_mmap,
17262306a36Sopenharmony_ci	.modify_port = pvrdma_modify_port,
17362306a36Sopenharmony_ci	.modify_qp = pvrdma_modify_qp,
17462306a36Sopenharmony_ci	.poll_cq = pvrdma_poll_cq,
17562306a36Sopenharmony_ci	.post_recv = pvrdma_post_recv,
17662306a36Sopenharmony_ci	.post_send = pvrdma_post_send,
17762306a36Sopenharmony_ci	.query_device = pvrdma_query_device,
17862306a36Sopenharmony_ci	.query_gid = pvrdma_query_gid,
17962306a36Sopenharmony_ci	.query_pkey = pvrdma_query_pkey,
18062306a36Sopenharmony_ci	.query_port = pvrdma_query_port,
18162306a36Sopenharmony_ci	.query_qp = pvrdma_query_qp,
18262306a36Sopenharmony_ci	.reg_user_mr = pvrdma_reg_user_mr,
18362306a36Sopenharmony_ci	.req_notify_cq = pvrdma_req_notify_cq,
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_ah, pvrdma_ah, ibah),
18662306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_cq, pvrdma_cq, ibcq),
18762306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_pd, pvrdma_pd, ibpd),
18862306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_qp, pvrdma_qp, ibqp),
18962306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_ucontext, pvrdma_ucontext, ibucontext),
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct ib_device_ops pvrdma_dev_srq_ops = {
19362306a36Sopenharmony_ci	.create_srq = pvrdma_create_srq,
19462306a36Sopenharmony_ci	.destroy_srq = pvrdma_destroy_srq,
19562306a36Sopenharmony_ci	.modify_srq = pvrdma_modify_srq,
19662306a36Sopenharmony_ci	.query_srq = pvrdma_query_srq,
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_srq, pvrdma_srq, ibsrq),
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int pvrdma_register_device(struct pvrdma_dev *dev)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret = -1;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dev->ib_dev.node_guid = dev->dsr->caps.node_guid;
20662306a36Sopenharmony_ci	dev->sys_image_guid = dev->dsr->caps.sys_image_guid;
20762306a36Sopenharmony_ci	dev->flags = 0;
20862306a36Sopenharmony_ci	dev->ib_dev.num_comp_vectors = 1;
20962306a36Sopenharmony_ci	dev->ib_dev.dev.parent = &dev->pdev->dev;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	dev->ib_dev.node_type = RDMA_NODE_IB_CA;
21262306a36Sopenharmony_ci	dev->ib_dev.phys_port_cnt = dev->dsr->caps.phys_port_cnt;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ib_set_device_ops(&dev->ib_dev, &pvrdma_dev_ops);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	mutex_init(&dev->port_mutex);
21762306a36Sopenharmony_ci	spin_lock_init(&dev->desc_lock);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	dev->cq_tbl = kcalloc(dev->dsr->caps.max_cq, sizeof(struct pvrdma_cq *),
22062306a36Sopenharmony_ci			      GFP_KERNEL);
22162306a36Sopenharmony_ci	if (!dev->cq_tbl)
22262306a36Sopenharmony_ci		return ret;
22362306a36Sopenharmony_ci	spin_lock_init(&dev->cq_tbl_lock);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	dev->qp_tbl = kcalloc(dev->dsr->caps.max_qp, sizeof(struct pvrdma_qp *),
22662306a36Sopenharmony_ci			      GFP_KERNEL);
22762306a36Sopenharmony_ci	if (!dev->qp_tbl)
22862306a36Sopenharmony_ci		goto err_cq_free;
22962306a36Sopenharmony_ci	spin_lock_init(&dev->qp_tbl_lock);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Check if SRQ is supported by backend */
23262306a36Sopenharmony_ci	if (dev->dsr->caps.max_srq) {
23362306a36Sopenharmony_ci		ib_set_device_ops(&dev->ib_dev, &pvrdma_dev_srq_ops);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		dev->srq_tbl = kcalloc(dev->dsr->caps.max_srq,
23662306a36Sopenharmony_ci				       sizeof(struct pvrdma_srq *),
23762306a36Sopenharmony_ci				       GFP_KERNEL);
23862306a36Sopenharmony_ci		if (!dev->srq_tbl)
23962306a36Sopenharmony_ci			goto err_qp_free;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	ret = ib_device_set_netdev(&dev->ib_dev, dev->netdev, 1);
24262306a36Sopenharmony_ci	if (ret)
24362306a36Sopenharmony_ci		goto err_srq_free;
24462306a36Sopenharmony_ci	spin_lock_init(&dev->srq_tbl_lock);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ret = ib_register_device(&dev->ib_dev, "vmw_pvrdma%d", &dev->pdev->dev);
24762306a36Sopenharmony_ci	if (ret)
24862306a36Sopenharmony_ci		goto err_srq_free;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	dev->ib_active = true;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cierr_srq_free:
25562306a36Sopenharmony_ci	kfree(dev->srq_tbl);
25662306a36Sopenharmony_cierr_qp_free:
25762306a36Sopenharmony_ci	kfree(dev->qp_tbl);
25862306a36Sopenharmony_cierr_cq_free:
25962306a36Sopenharmony_ci	kfree(dev->cq_tbl);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic irqreturn_t pvrdma_intr0_handler(int irq, void *dev_id)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	u32 icr = PVRDMA_INTR_CAUSE_RESPONSE;
26762306a36Sopenharmony_ci	struct pvrdma_dev *dev = dev_id;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "interrupt 0 (response) handler\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (!dev->pdev->msix_enabled) {
27262306a36Sopenharmony_ci		/* Legacy intr */
27362306a36Sopenharmony_ci		icr = pvrdma_read_reg(dev, PVRDMA_REG_ICR);
27462306a36Sopenharmony_ci		if (icr == 0)
27562306a36Sopenharmony_ci			return IRQ_NONE;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (icr == PVRDMA_INTR_CAUSE_RESPONSE)
27962306a36Sopenharmony_ci		complete(&dev->cmd_done);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	return IRQ_HANDLED;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void pvrdma_qp_event(struct pvrdma_dev *dev, u32 qpn, int type)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct pvrdma_qp *qp;
28762306a36Sopenharmony_ci	unsigned long flags;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->qp_tbl_lock, flags);
29062306a36Sopenharmony_ci	qp = dev->qp_tbl[qpn % dev->dsr->caps.max_qp];
29162306a36Sopenharmony_ci	if (qp)
29262306a36Sopenharmony_ci		refcount_inc(&qp->refcnt);
29362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->qp_tbl_lock, flags);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (qp && qp->ibqp.event_handler) {
29662306a36Sopenharmony_ci		struct ib_qp *ibqp = &qp->ibqp;
29762306a36Sopenharmony_ci		struct ib_event e;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		e.device = ibqp->device;
30062306a36Sopenharmony_ci		e.element.qp = ibqp;
30162306a36Sopenharmony_ci		e.event = type; /* 1:1 mapping for now. */
30262306a36Sopenharmony_ci		ibqp->event_handler(&e, ibqp->qp_context);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci	if (qp) {
30562306a36Sopenharmony_ci		if (refcount_dec_and_test(&qp->refcnt))
30662306a36Sopenharmony_ci			complete(&qp->free);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void pvrdma_cq_event(struct pvrdma_dev *dev, u32 cqn, int type)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct pvrdma_cq *cq;
31362306a36Sopenharmony_ci	unsigned long flags;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	spin_lock_irqsave(&dev->cq_tbl_lock, flags);
31662306a36Sopenharmony_ci	cq = dev->cq_tbl[cqn % dev->dsr->caps.max_cq];
31762306a36Sopenharmony_ci	if (cq)
31862306a36Sopenharmony_ci		refcount_inc(&cq->refcnt);
31962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->cq_tbl_lock, flags);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (cq && cq->ibcq.event_handler) {
32262306a36Sopenharmony_ci		struct ib_cq *ibcq = &cq->ibcq;
32362306a36Sopenharmony_ci		struct ib_event e;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		e.device = ibcq->device;
32662306a36Sopenharmony_ci		e.element.cq = ibcq;
32762306a36Sopenharmony_ci		e.event = type; /* 1:1 mapping for now. */
32862306a36Sopenharmony_ci		ibcq->event_handler(&e, ibcq->cq_context);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	if (cq) {
33162306a36Sopenharmony_ci		if (refcount_dec_and_test(&cq->refcnt))
33262306a36Sopenharmony_ci			complete(&cq->free);
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void pvrdma_srq_event(struct pvrdma_dev *dev, u32 srqn, int type)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct pvrdma_srq *srq;
33962306a36Sopenharmony_ci	unsigned long flags;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->srq_tbl_lock, flags);
34262306a36Sopenharmony_ci	if (dev->srq_tbl)
34362306a36Sopenharmony_ci		srq = dev->srq_tbl[srqn % dev->dsr->caps.max_srq];
34462306a36Sopenharmony_ci	else
34562306a36Sopenharmony_ci		srq = NULL;
34662306a36Sopenharmony_ci	if (srq)
34762306a36Sopenharmony_ci		refcount_inc(&srq->refcnt);
34862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->srq_tbl_lock, flags);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (srq && srq->ibsrq.event_handler) {
35162306a36Sopenharmony_ci		struct ib_srq *ibsrq = &srq->ibsrq;
35262306a36Sopenharmony_ci		struct ib_event e;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		e.device = ibsrq->device;
35562306a36Sopenharmony_ci		e.element.srq = ibsrq;
35662306a36Sopenharmony_ci		e.event = type; /* 1:1 mapping for now. */
35762306a36Sopenharmony_ci		ibsrq->event_handler(&e, ibsrq->srq_context);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci	if (srq) {
36062306a36Sopenharmony_ci		if (refcount_dec_and_test(&srq->refcnt))
36162306a36Sopenharmony_ci			complete(&srq->free);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void pvrdma_dispatch_event(struct pvrdma_dev *dev, int port,
36662306a36Sopenharmony_ci				  enum ib_event_type event)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct ib_event ib_event;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	memset(&ib_event, 0, sizeof(ib_event));
37162306a36Sopenharmony_ci	ib_event.device = &dev->ib_dev;
37262306a36Sopenharmony_ci	ib_event.element.port_num = port;
37362306a36Sopenharmony_ci	ib_event.event = event;
37462306a36Sopenharmony_ci	ib_dispatch_event(&ib_event);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void pvrdma_dev_event(struct pvrdma_dev *dev, u8 port, int type)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	if (port < 1 || port > dev->dsr->caps.phys_port_cnt) {
38062306a36Sopenharmony_ci		dev_warn(&dev->pdev->dev, "event on port %d\n", port);
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	pvrdma_dispatch_event(dev, port, type);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic inline struct pvrdma_eqe *get_eqe(struct pvrdma_dev *dev, unsigned int i)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	return (struct pvrdma_eqe *)pvrdma_page_dir_get_ptr(
39062306a36Sopenharmony_ci					&dev->async_pdir,
39162306a36Sopenharmony_ci					PAGE_SIZE +
39262306a36Sopenharmony_ci					sizeof(struct pvrdma_eqe) * i);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic irqreturn_t pvrdma_intr1_handler(int irq, void *dev_id)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct pvrdma_dev *dev = dev_id;
39862306a36Sopenharmony_ci	struct pvrdma_ring *ring = &dev->async_ring_state->rx;
39962306a36Sopenharmony_ci	int ring_slots = (dev->dsr->async_ring_pages.num_pages - 1) *
40062306a36Sopenharmony_ci			 PAGE_SIZE / sizeof(struct pvrdma_eqe);
40162306a36Sopenharmony_ci	unsigned int head;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "interrupt 1 (async event) handler\n");
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/*
40662306a36Sopenharmony_ci	 * Don't process events until the IB device is registered. Otherwise
40762306a36Sopenharmony_ci	 * we'll try to ib_dispatch_event() on an invalid device.
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	if (!dev->ib_active)
41062306a36Sopenharmony_ci		return IRQ_HANDLED;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	while (pvrdma_idx_ring_has_data(ring, ring_slots, &head) > 0) {
41362306a36Sopenharmony_ci		struct pvrdma_eqe *eqe;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		eqe = get_eqe(dev, head);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		switch (eqe->type) {
41862306a36Sopenharmony_ci		case PVRDMA_EVENT_QP_FATAL:
41962306a36Sopenharmony_ci		case PVRDMA_EVENT_QP_REQ_ERR:
42062306a36Sopenharmony_ci		case PVRDMA_EVENT_QP_ACCESS_ERR:
42162306a36Sopenharmony_ci		case PVRDMA_EVENT_COMM_EST:
42262306a36Sopenharmony_ci		case PVRDMA_EVENT_SQ_DRAINED:
42362306a36Sopenharmony_ci		case PVRDMA_EVENT_PATH_MIG:
42462306a36Sopenharmony_ci		case PVRDMA_EVENT_PATH_MIG_ERR:
42562306a36Sopenharmony_ci		case PVRDMA_EVENT_QP_LAST_WQE_REACHED:
42662306a36Sopenharmony_ci			pvrdma_qp_event(dev, eqe->info, eqe->type);
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		case PVRDMA_EVENT_CQ_ERR:
43062306a36Sopenharmony_ci			pvrdma_cq_event(dev, eqe->info, eqe->type);
43162306a36Sopenharmony_ci			break;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		case PVRDMA_EVENT_SRQ_ERR:
43462306a36Sopenharmony_ci		case PVRDMA_EVENT_SRQ_LIMIT_REACHED:
43562306a36Sopenharmony_ci			pvrdma_srq_event(dev, eqe->info, eqe->type);
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		case PVRDMA_EVENT_PORT_ACTIVE:
43962306a36Sopenharmony_ci		case PVRDMA_EVENT_PORT_ERR:
44062306a36Sopenharmony_ci		case PVRDMA_EVENT_LID_CHANGE:
44162306a36Sopenharmony_ci		case PVRDMA_EVENT_PKEY_CHANGE:
44262306a36Sopenharmony_ci		case PVRDMA_EVENT_SM_CHANGE:
44362306a36Sopenharmony_ci		case PVRDMA_EVENT_CLIENT_REREGISTER:
44462306a36Sopenharmony_ci		case PVRDMA_EVENT_GID_CHANGE:
44562306a36Sopenharmony_ci			pvrdma_dev_event(dev, eqe->info, eqe->type);
44662306a36Sopenharmony_ci			break;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		case PVRDMA_EVENT_DEVICE_FATAL:
44962306a36Sopenharmony_ci			pvrdma_dev_event(dev, 1, eqe->type);
45062306a36Sopenharmony_ci			break;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		default:
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		pvrdma_idx_ring_inc(&ring->cons_head, ring_slots);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return IRQ_HANDLED;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic inline struct pvrdma_cqne *get_cqne(struct pvrdma_dev *dev,
46362306a36Sopenharmony_ci					   unsigned int i)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	return (struct pvrdma_cqne *)pvrdma_page_dir_get_ptr(
46662306a36Sopenharmony_ci					&dev->cq_pdir,
46762306a36Sopenharmony_ci					PAGE_SIZE +
46862306a36Sopenharmony_ci					sizeof(struct pvrdma_cqne) * i);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic irqreturn_t pvrdma_intrx_handler(int irq, void *dev_id)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct pvrdma_dev *dev = dev_id;
47462306a36Sopenharmony_ci	struct pvrdma_ring *ring = &dev->cq_ring_state->rx;
47562306a36Sopenharmony_ci	int ring_slots = (dev->dsr->cq_ring_pages.num_pages - 1) * PAGE_SIZE /
47662306a36Sopenharmony_ci			 sizeof(struct pvrdma_cqne);
47762306a36Sopenharmony_ci	unsigned int head;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "interrupt x (completion) handler\n");
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	while (pvrdma_idx_ring_has_data(ring, ring_slots, &head) > 0) {
48262306a36Sopenharmony_ci		struct pvrdma_cqne *cqne;
48362306a36Sopenharmony_ci		struct pvrdma_cq *cq;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		cqne = get_cqne(dev, head);
48662306a36Sopenharmony_ci		spin_lock(&dev->cq_tbl_lock);
48762306a36Sopenharmony_ci		cq = dev->cq_tbl[cqne->info % dev->dsr->caps.max_cq];
48862306a36Sopenharmony_ci		if (cq)
48962306a36Sopenharmony_ci			refcount_inc(&cq->refcnt);
49062306a36Sopenharmony_ci		spin_unlock(&dev->cq_tbl_lock);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		if (cq && cq->ibcq.comp_handler)
49362306a36Sopenharmony_ci			cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
49462306a36Sopenharmony_ci		if (cq) {
49562306a36Sopenharmony_ci			if (refcount_dec_and_test(&cq->refcnt))
49662306a36Sopenharmony_ci				complete(&cq->free);
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci		pvrdma_idx_ring_inc(&ring->cons_head, ring_slots);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return IRQ_HANDLED;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void pvrdma_free_irq(struct pvrdma_dev *dev)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	int i;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "freeing interrupts\n");
50962306a36Sopenharmony_ci	for (i = 0; i < dev->nr_vectors; i++)
51062306a36Sopenharmony_ci		free_irq(pci_irq_vector(dev->pdev, i), dev);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void pvrdma_enable_intrs(struct pvrdma_dev *dev)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "enable interrupts\n");
51662306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_IMR, 0);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic void pvrdma_disable_intrs(struct pvrdma_dev *dev)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "disable interrupts\n");
52262306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_IMR, ~0);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic int pvrdma_alloc_intrs(struct pvrdma_dev *dev)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct pci_dev *pdev = dev->pdev;
52862306a36Sopenharmony_ci	int ret = 0, i;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, 1, PVRDMA_MAX_INTERRUPTS,
53162306a36Sopenharmony_ci			PCI_IRQ_MSIX);
53262306a36Sopenharmony_ci	if (ret < 0) {
53362306a36Sopenharmony_ci		ret = pci_alloc_irq_vectors(pdev, 1, 1,
53462306a36Sopenharmony_ci				PCI_IRQ_MSI | PCI_IRQ_LEGACY);
53562306a36Sopenharmony_ci		if (ret < 0)
53662306a36Sopenharmony_ci			return ret;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	dev->nr_vectors = ret;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ret = request_irq(pci_irq_vector(dev->pdev, 0), pvrdma_intr0_handler,
54162306a36Sopenharmony_ci			pdev->msix_enabled ? 0 : IRQF_SHARED, DRV_NAME, dev);
54262306a36Sopenharmony_ci	if (ret) {
54362306a36Sopenharmony_ci		dev_err(&dev->pdev->dev,
54462306a36Sopenharmony_ci			"failed to request interrupt 0\n");
54562306a36Sopenharmony_ci		goto out_free_vectors;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	for (i = 1; i < dev->nr_vectors; i++) {
54962306a36Sopenharmony_ci		ret = request_irq(pci_irq_vector(dev->pdev, i),
55062306a36Sopenharmony_ci				i == 1 ? pvrdma_intr1_handler :
55162306a36Sopenharmony_ci					 pvrdma_intrx_handler,
55262306a36Sopenharmony_ci				0, DRV_NAME, dev);
55362306a36Sopenharmony_ci		if (ret) {
55462306a36Sopenharmony_ci			dev_err(&dev->pdev->dev,
55562306a36Sopenharmony_ci				"failed to request interrupt %d\n", i);
55662306a36Sopenharmony_ci			goto free_irqs;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cifree_irqs:
56362306a36Sopenharmony_ci	while (--i >= 0)
56462306a36Sopenharmony_ci		free_irq(pci_irq_vector(dev->pdev, i), dev);
56562306a36Sopenharmony_ciout_free_vectors:
56662306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
56762306a36Sopenharmony_ci	return ret;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic void pvrdma_free_slots(struct pvrdma_dev *dev)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct pci_dev *pdev = dev->pdev;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (dev->resp_slot)
57562306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->resp_slot,
57662306a36Sopenharmony_ci				  dev->dsr->resp_slot_dma);
57762306a36Sopenharmony_ci	if (dev->cmd_slot)
57862306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->cmd_slot,
57962306a36Sopenharmony_ci				  dev->dsr->cmd_slot_dma);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int pvrdma_add_gid_at_index(struct pvrdma_dev *dev,
58362306a36Sopenharmony_ci				   const union ib_gid *gid,
58462306a36Sopenharmony_ci				   u8 gid_type,
58562306a36Sopenharmony_ci				   int index)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	int ret;
58862306a36Sopenharmony_ci	union pvrdma_cmd_req req;
58962306a36Sopenharmony_ci	struct pvrdma_cmd_create_bind *cmd_bind = &req.create_bind;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (!dev->sgid_tbl) {
59262306a36Sopenharmony_ci		dev_warn(&dev->pdev->dev, "sgid table not initialized\n");
59362306a36Sopenharmony_ci		return -EINVAL;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	memset(cmd_bind, 0, sizeof(*cmd_bind));
59762306a36Sopenharmony_ci	cmd_bind->hdr.cmd = PVRDMA_CMD_CREATE_BIND;
59862306a36Sopenharmony_ci	memcpy(cmd_bind->new_gid, gid->raw, 16);
59962306a36Sopenharmony_ci	cmd_bind->mtu = ib_mtu_enum_to_int(IB_MTU_1024);
60062306a36Sopenharmony_ci	cmd_bind->vlan = 0xfff;
60162306a36Sopenharmony_ci	cmd_bind->index = index;
60262306a36Sopenharmony_ci	cmd_bind->gid_type = gid_type;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	ret = pvrdma_cmd_post(dev, &req, NULL, 0);
60562306a36Sopenharmony_ci	if (ret < 0) {
60662306a36Sopenharmony_ci		dev_warn(&dev->pdev->dev,
60762306a36Sopenharmony_ci			 "could not create binding, error: %d\n", ret);
60862306a36Sopenharmony_ci		return -EFAULT;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	memcpy(&dev->sgid_tbl[index], gid, sizeof(*gid));
61162306a36Sopenharmony_ci	return 0;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int pvrdma_add_gid(const struct ib_gid_attr *attr, void **context)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(attr->device);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return pvrdma_add_gid_at_index(dev, &attr->gid,
61962306a36Sopenharmony_ci				       ib_gid_type_to_pvrdma(attr->gid_type),
62062306a36Sopenharmony_ci				       attr->index);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int pvrdma_del_gid_at_index(struct pvrdma_dev *dev, int index)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci	union pvrdma_cmd_req req;
62762306a36Sopenharmony_ci	struct pvrdma_cmd_destroy_bind *cmd_dest = &req.destroy_bind;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Update sgid table. */
63062306a36Sopenharmony_ci	if (!dev->sgid_tbl) {
63162306a36Sopenharmony_ci		dev_warn(&dev->pdev->dev, "sgid table not initialized\n");
63262306a36Sopenharmony_ci		return -EINVAL;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	memset(cmd_dest, 0, sizeof(*cmd_dest));
63662306a36Sopenharmony_ci	cmd_dest->hdr.cmd = PVRDMA_CMD_DESTROY_BIND;
63762306a36Sopenharmony_ci	memcpy(cmd_dest->dest_gid, &dev->sgid_tbl[index], 16);
63862306a36Sopenharmony_ci	cmd_dest->index = index;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	ret = pvrdma_cmd_post(dev, &req, NULL, 0);
64162306a36Sopenharmony_ci	if (ret < 0) {
64262306a36Sopenharmony_ci		dev_warn(&dev->pdev->dev,
64362306a36Sopenharmony_ci			 "could not destroy binding, error: %d\n", ret);
64462306a36Sopenharmony_ci		return ret;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	memset(&dev->sgid_tbl[index], 0, 16);
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic int pvrdma_del_gid(const struct ib_gid_attr *attr, void **context)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct pvrdma_dev *dev = to_vdev(attr->device);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	dev_dbg(&dev->pdev->dev, "removing gid at index %u from %s",
65562306a36Sopenharmony_ci		attr->index, dev->netdev->name);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return pvrdma_del_gid_at_index(dev, attr->index);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic void pvrdma_netdevice_event_handle(struct pvrdma_dev *dev,
66162306a36Sopenharmony_ci					  struct net_device *ndev,
66262306a36Sopenharmony_ci					  unsigned long event)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct pci_dev *pdev_net;
66562306a36Sopenharmony_ci	unsigned int slot;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	switch (event) {
66862306a36Sopenharmony_ci	case NETDEV_REBOOT:
66962306a36Sopenharmony_ci	case NETDEV_DOWN:
67062306a36Sopenharmony_ci		pvrdma_dispatch_event(dev, 1, IB_EVENT_PORT_ERR);
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case NETDEV_UP:
67362306a36Sopenharmony_ci		pvrdma_write_reg(dev, PVRDMA_REG_CTL,
67462306a36Sopenharmony_ci				 PVRDMA_DEVICE_CTL_UNQUIESCE);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		mb();
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		if (pvrdma_read_reg(dev, PVRDMA_REG_ERR))
67962306a36Sopenharmony_ci			dev_err(&dev->pdev->dev,
68062306a36Sopenharmony_ci				"failed to activate device during link up\n");
68162306a36Sopenharmony_ci		else
68262306a36Sopenharmony_ci			pvrdma_dispatch_event(dev, 1, IB_EVENT_PORT_ACTIVE);
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
68562306a36Sopenharmony_ci		ib_device_set_netdev(&dev->ib_dev, NULL, 1);
68662306a36Sopenharmony_ci		dev_put(dev->netdev);
68762306a36Sopenharmony_ci		dev->netdev = NULL;
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci	case NETDEV_REGISTER:
69062306a36Sopenharmony_ci		/* vmxnet3 will have same bus, slot. But func will be 0 */
69162306a36Sopenharmony_ci		slot = PCI_SLOT(dev->pdev->devfn);
69262306a36Sopenharmony_ci		pdev_net = pci_get_slot(dev->pdev->bus,
69362306a36Sopenharmony_ci					PCI_DEVFN(slot, 0));
69462306a36Sopenharmony_ci		if ((dev->netdev == NULL) &&
69562306a36Sopenharmony_ci		    (pci_get_drvdata(pdev_net) == ndev)) {
69662306a36Sopenharmony_ci			/* this is our netdev */
69762306a36Sopenharmony_ci			ib_device_set_netdev(&dev->ib_dev, ndev, 1);
69862306a36Sopenharmony_ci			dev->netdev = ndev;
69962306a36Sopenharmony_ci			dev_hold(ndev);
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci		pci_dev_put(pdev_net);
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	default:
70562306a36Sopenharmony_ci		dev_dbg(&dev->pdev->dev, "ignore netdevice event %ld on %s\n",
70662306a36Sopenharmony_ci			event, dev_name(&dev->ib_dev.dev));
70762306a36Sopenharmony_ci		break;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void pvrdma_netdevice_event_work(struct work_struct *work)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct pvrdma_netdevice_work *netdev_work;
71462306a36Sopenharmony_ci	struct pvrdma_dev *dev;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	netdev_work = container_of(work, struct pvrdma_netdevice_work, work);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	mutex_lock(&pvrdma_device_list_lock);
71962306a36Sopenharmony_ci	list_for_each_entry(dev, &pvrdma_device_list, device_link) {
72062306a36Sopenharmony_ci		if ((netdev_work->event == NETDEV_REGISTER) ||
72162306a36Sopenharmony_ci		    (dev->netdev == netdev_work->event_netdev)) {
72262306a36Sopenharmony_ci			pvrdma_netdevice_event_handle(dev,
72362306a36Sopenharmony_ci						      netdev_work->event_netdev,
72462306a36Sopenharmony_ci						      netdev_work->event);
72562306a36Sopenharmony_ci			break;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci	mutex_unlock(&pvrdma_device_list_lock);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	kfree(netdev_work);
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic int pvrdma_netdevice_event(struct notifier_block *this,
73462306a36Sopenharmony_ci				  unsigned long event, void *ptr)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct net_device *event_netdev = netdev_notifier_info_to_dev(ptr);
73762306a36Sopenharmony_ci	struct pvrdma_netdevice_work *netdev_work;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	netdev_work = kmalloc(sizeof(*netdev_work), GFP_ATOMIC);
74062306a36Sopenharmony_ci	if (!netdev_work)
74162306a36Sopenharmony_ci		return NOTIFY_BAD;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	INIT_WORK(&netdev_work->work, pvrdma_netdevice_event_work);
74462306a36Sopenharmony_ci	netdev_work->event_netdev = event_netdev;
74562306a36Sopenharmony_ci	netdev_work->event = event;
74662306a36Sopenharmony_ci	queue_work(event_wq, &netdev_work->work);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return NOTIFY_DONE;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic int pvrdma_pci_probe(struct pci_dev *pdev,
75262306a36Sopenharmony_ci			    const struct pci_device_id *id)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	struct pci_dev *pdev_net;
75562306a36Sopenharmony_ci	struct pvrdma_dev *dev;
75662306a36Sopenharmony_ci	int ret;
75762306a36Sopenharmony_ci	unsigned long start;
75862306a36Sopenharmony_ci	unsigned long len;
75962306a36Sopenharmony_ci	dma_addr_t slot_dma = 0;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "initializing driver %s\n", pci_name(pdev));
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* Allocate zero-out device */
76462306a36Sopenharmony_ci	dev = ib_alloc_device(pvrdma_dev, ib_dev);
76562306a36Sopenharmony_ci	if (!dev) {
76662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate IB device\n");
76762306a36Sopenharmony_ci		return -ENOMEM;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	mutex_lock(&pvrdma_device_list_lock);
77162306a36Sopenharmony_ci	list_add(&dev->device_link, &pvrdma_device_list);
77262306a36Sopenharmony_ci	mutex_unlock(&pvrdma_device_list_lock);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	ret = pvrdma_init_device(dev);
77562306a36Sopenharmony_ci	if (ret)
77662306a36Sopenharmony_ci		goto err_free_device;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	dev->pdev = pdev;
77962306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
78262306a36Sopenharmony_ci	if (ret) {
78362306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot enable PCI device\n");
78462306a36Sopenharmony_ci		goto err_free_device;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource flags BAR0 %#lx\n",
78862306a36Sopenharmony_ci		pci_resource_flags(pdev, 0));
78962306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource len %#llx\n",
79062306a36Sopenharmony_ci		(unsigned long long)pci_resource_len(pdev, 0));
79162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource start %#llx\n",
79262306a36Sopenharmony_ci		(unsigned long long)pci_resource_start(pdev, 0));
79362306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource flags BAR1 %#lx\n",
79462306a36Sopenharmony_ci		pci_resource_flags(pdev, 1));
79562306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource len %#llx\n",
79662306a36Sopenharmony_ci		(unsigned long long)pci_resource_len(pdev, 1));
79762306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "PCI resource start %#llx\n",
79862306a36Sopenharmony_ci		(unsigned long long)pci_resource_start(pdev, 1));
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
80162306a36Sopenharmony_ci	    !(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
80262306a36Sopenharmony_ci		dev_err(&pdev->dev, "PCI BAR region not MMIO\n");
80362306a36Sopenharmony_ci		ret = -ENOMEM;
80462306a36Sopenharmony_ci		goto err_disable_pdev;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	ret = pci_request_regions(pdev, DRV_NAME);
80862306a36Sopenharmony_ci	if (ret) {
80962306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot request PCI resources\n");
81062306a36Sopenharmony_ci		goto err_disable_pdev;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* Enable 64-Bit DMA */
81462306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
81562306a36Sopenharmony_ci	if (ret) {
81662306a36Sopenharmony_ci		dev_err(&pdev->dev, "dma_set_mask failed\n");
81762306a36Sopenharmony_ci		goto err_free_resource;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
82062306a36Sopenharmony_ci	pci_set_master(pdev);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	/* Map register space */
82362306a36Sopenharmony_ci	start = pci_resource_start(dev->pdev, PVRDMA_PCI_RESOURCE_REG);
82462306a36Sopenharmony_ci	len = pci_resource_len(dev->pdev, PVRDMA_PCI_RESOURCE_REG);
82562306a36Sopenharmony_ci	dev->regs = ioremap(start, len);
82662306a36Sopenharmony_ci	if (!dev->regs) {
82762306a36Sopenharmony_ci		dev_err(&pdev->dev, "register mapping failed\n");
82862306a36Sopenharmony_ci		ret = -ENOMEM;
82962306a36Sopenharmony_ci		goto err_free_resource;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Setup per-device UAR. */
83362306a36Sopenharmony_ci	dev->driver_uar.index = 0;
83462306a36Sopenharmony_ci	dev->driver_uar.pfn =
83562306a36Sopenharmony_ci		pci_resource_start(dev->pdev, PVRDMA_PCI_RESOURCE_UAR) >>
83662306a36Sopenharmony_ci		PAGE_SHIFT;
83762306a36Sopenharmony_ci	dev->driver_uar.map =
83862306a36Sopenharmony_ci		ioremap(dev->driver_uar.pfn << PAGE_SHIFT, PAGE_SIZE);
83962306a36Sopenharmony_ci	if (!dev->driver_uar.map) {
84062306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to remap UAR pages\n");
84162306a36Sopenharmony_ci		ret = -ENOMEM;
84262306a36Sopenharmony_ci		goto err_unmap_regs;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	dev->dsr_version = pvrdma_read_reg(dev, PVRDMA_REG_VERSION);
84662306a36Sopenharmony_ci	dev_info(&pdev->dev, "device version %d, driver version %d\n",
84762306a36Sopenharmony_ci		 dev->dsr_version, PVRDMA_VERSION);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	dev->dsr = dma_alloc_coherent(&pdev->dev, sizeof(*dev->dsr),
85062306a36Sopenharmony_ci				      &dev->dsrbase, GFP_KERNEL);
85162306a36Sopenharmony_ci	if (!dev->dsr) {
85262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate shared region\n");
85362306a36Sopenharmony_ci		ret = -ENOMEM;
85462306a36Sopenharmony_ci		goto err_uar_unmap;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* Setup the shared region */
85862306a36Sopenharmony_ci	dev->dsr->driver_version = PVRDMA_VERSION;
85962306a36Sopenharmony_ci	dev->dsr->gos_info.gos_bits = sizeof(void *) == 4 ?
86062306a36Sopenharmony_ci		PVRDMA_GOS_BITS_32 :
86162306a36Sopenharmony_ci		PVRDMA_GOS_BITS_64;
86262306a36Sopenharmony_ci	dev->dsr->gos_info.gos_type = PVRDMA_GOS_TYPE_LINUX;
86362306a36Sopenharmony_ci	dev->dsr->gos_info.gos_ver = 1;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (dev->dsr_version < PVRDMA_PPN64_VERSION)
86662306a36Sopenharmony_ci		dev->dsr->uar_pfn = dev->driver_uar.pfn;
86762306a36Sopenharmony_ci	else
86862306a36Sopenharmony_ci		dev->dsr->uar_pfn64 = dev->driver_uar.pfn;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/* Command slot. */
87162306a36Sopenharmony_ci	dev->cmd_slot = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
87262306a36Sopenharmony_ci					   &slot_dma, GFP_KERNEL);
87362306a36Sopenharmony_ci	if (!dev->cmd_slot) {
87462306a36Sopenharmony_ci		ret = -ENOMEM;
87562306a36Sopenharmony_ci		goto err_free_dsr;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	dev->dsr->cmd_slot_dma = (u64)slot_dma;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/* Response slot. */
88162306a36Sopenharmony_ci	dev->resp_slot = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
88262306a36Sopenharmony_ci					    &slot_dma, GFP_KERNEL);
88362306a36Sopenharmony_ci	if (!dev->resp_slot) {
88462306a36Sopenharmony_ci		ret = -ENOMEM;
88562306a36Sopenharmony_ci		goto err_free_slots;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	dev->dsr->resp_slot_dma = (u64)slot_dma;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	/* Async event ring */
89162306a36Sopenharmony_ci	dev->dsr->async_ring_pages.num_pages = PVRDMA_NUM_RING_PAGES;
89262306a36Sopenharmony_ci	ret = pvrdma_page_dir_init(dev, &dev->async_pdir,
89362306a36Sopenharmony_ci				   dev->dsr->async_ring_pages.num_pages, true);
89462306a36Sopenharmony_ci	if (ret)
89562306a36Sopenharmony_ci		goto err_free_slots;
89662306a36Sopenharmony_ci	dev->async_ring_state = dev->async_pdir.pages[0];
89762306a36Sopenharmony_ci	dev->dsr->async_ring_pages.pdir_dma = dev->async_pdir.dir_dma;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* CQ notification ring */
90062306a36Sopenharmony_ci	dev->dsr->cq_ring_pages.num_pages = PVRDMA_NUM_RING_PAGES;
90162306a36Sopenharmony_ci	ret = pvrdma_page_dir_init(dev, &dev->cq_pdir,
90262306a36Sopenharmony_ci				   dev->dsr->cq_ring_pages.num_pages, true);
90362306a36Sopenharmony_ci	if (ret)
90462306a36Sopenharmony_ci		goto err_free_async_ring;
90562306a36Sopenharmony_ci	dev->cq_ring_state = dev->cq_pdir.pages[0];
90662306a36Sopenharmony_ci	dev->dsr->cq_ring_pages.pdir_dma = dev->cq_pdir.dir_dma;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/*
90962306a36Sopenharmony_ci	 * Write the PA of the shared region to the device. The writes must be
91062306a36Sopenharmony_ci	 * ordered such that the high bits are written last. When the writes
91162306a36Sopenharmony_ci	 * complete, the device will have filled out the capabilities.
91262306a36Sopenharmony_ci	 */
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_DSRLOW, (u32)dev->dsrbase);
91562306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_DSRHIGH,
91662306a36Sopenharmony_ci			 (u32)((u64)(dev->dsrbase) >> 32));
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* Make sure the write is complete before reading status. */
91962306a36Sopenharmony_ci	mb();
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* The driver supports RoCE V1 and V2. */
92262306a36Sopenharmony_ci	if (!PVRDMA_SUPPORTED(dev)) {
92362306a36Sopenharmony_ci		dev_err(&pdev->dev, "driver needs RoCE v1 or v2 support\n");
92462306a36Sopenharmony_ci		ret = -EFAULT;
92562306a36Sopenharmony_ci		goto err_free_cq_ring;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* Paired vmxnet3 will have same bus, slot. But func will be 0 */
92962306a36Sopenharmony_ci	pdev_net = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 0));
93062306a36Sopenharmony_ci	if (!pdev_net) {
93162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to find paired net device\n");
93262306a36Sopenharmony_ci		ret = -ENODEV;
93362306a36Sopenharmony_ci		goto err_free_cq_ring;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (pdev_net->vendor != PCI_VENDOR_ID_VMWARE ||
93762306a36Sopenharmony_ci	    pdev_net->device != PCI_DEVICE_ID_VMWARE_VMXNET3) {
93862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to find paired vmxnet3 device\n");
93962306a36Sopenharmony_ci		pci_dev_put(pdev_net);
94062306a36Sopenharmony_ci		ret = -ENODEV;
94162306a36Sopenharmony_ci		goto err_free_cq_ring;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	dev->netdev = pci_get_drvdata(pdev_net);
94562306a36Sopenharmony_ci	pci_dev_put(pdev_net);
94662306a36Sopenharmony_ci	if (!dev->netdev) {
94762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get vmxnet3 device\n");
94862306a36Sopenharmony_ci		ret = -ENODEV;
94962306a36Sopenharmony_ci		goto err_free_cq_ring;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci	dev_hold(dev->netdev);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	dev_info(&pdev->dev, "paired device to %s\n", dev->netdev->name);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Interrupt setup */
95662306a36Sopenharmony_ci	ret = pvrdma_alloc_intrs(dev);
95762306a36Sopenharmony_ci	if (ret) {
95862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate interrupts\n");
95962306a36Sopenharmony_ci		ret = -ENOMEM;
96062306a36Sopenharmony_ci		goto err_free_cq_ring;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* Allocate UAR table. */
96462306a36Sopenharmony_ci	ret = pvrdma_uar_table_init(dev);
96562306a36Sopenharmony_ci	if (ret) {
96662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate UAR table\n");
96762306a36Sopenharmony_ci		ret = -ENOMEM;
96862306a36Sopenharmony_ci		goto err_free_intrs;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	/* Allocate GID table */
97262306a36Sopenharmony_ci	dev->sgid_tbl = kcalloc(dev->dsr->caps.gid_tbl_len,
97362306a36Sopenharmony_ci				sizeof(union ib_gid), GFP_KERNEL);
97462306a36Sopenharmony_ci	if (!dev->sgid_tbl) {
97562306a36Sopenharmony_ci		ret = -ENOMEM;
97662306a36Sopenharmony_ci		goto err_free_uar_table;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "gid table len %d\n", dev->dsr->caps.gid_tbl_len);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	pvrdma_enable_intrs(dev);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Activate pvrdma device */
98362306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_CTL, PVRDMA_DEVICE_CTL_ACTIVATE);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Make sure the write is complete before reading status. */
98662306a36Sopenharmony_ci	mb();
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* Check if device was successfully activated */
98962306a36Sopenharmony_ci	ret = pvrdma_read_reg(dev, PVRDMA_REG_ERR);
99062306a36Sopenharmony_ci	if (ret != 0) {
99162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to activate device\n");
99262306a36Sopenharmony_ci		ret = -EFAULT;
99362306a36Sopenharmony_ci		goto err_disable_intr;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* Register IB device */
99762306a36Sopenharmony_ci	ret = pvrdma_register_device(dev);
99862306a36Sopenharmony_ci	if (ret) {
99962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register IB device\n");
100062306a36Sopenharmony_ci		goto err_disable_intr;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	dev->nb_netdev.notifier_call = pvrdma_netdevice_event;
100462306a36Sopenharmony_ci	ret = register_netdevice_notifier(&dev->nb_netdev);
100562306a36Sopenharmony_ci	if (ret) {
100662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register netdevice events\n");
100762306a36Sopenharmony_ci		goto err_unreg_ibdev;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	dev_info(&pdev->dev, "attached to device\n");
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cierr_unreg_ibdev:
101462306a36Sopenharmony_ci	ib_unregister_device(&dev->ib_dev);
101562306a36Sopenharmony_cierr_disable_intr:
101662306a36Sopenharmony_ci	pvrdma_disable_intrs(dev);
101762306a36Sopenharmony_ci	kfree(dev->sgid_tbl);
101862306a36Sopenharmony_cierr_free_uar_table:
101962306a36Sopenharmony_ci	pvrdma_uar_table_cleanup(dev);
102062306a36Sopenharmony_cierr_free_intrs:
102162306a36Sopenharmony_ci	pvrdma_free_irq(dev);
102262306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
102362306a36Sopenharmony_cierr_free_cq_ring:
102462306a36Sopenharmony_ci	if (dev->netdev) {
102562306a36Sopenharmony_ci		dev_put(dev->netdev);
102662306a36Sopenharmony_ci		dev->netdev = NULL;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	pvrdma_page_dir_cleanup(dev, &dev->cq_pdir);
102962306a36Sopenharmony_cierr_free_async_ring:
103062306a36Sopenharmony_ci	pvrdma_page_dir_cleanup(dev, &dev->async_pdir);
103162306a36Sopenharmony_cierr_free_slots:
103262306a36Sopenharmony_ci	pvrdma_free_slots(dev);
103362306a36Sopenharmony_cierr_free_dsr:
103462306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(*dev->dsr), dev->dsr,
103562306a36Sopenharmony_ci			  dev->dsrbase);
103662306a36Sopenharmony_cierr_uar_unmap:
103762306a36Sopenharmony_ci	iounmap(dev->driver_uar.map);
103862306a36Sopenharmony_cierr_unmap_regs:
103962306a36Sopenharmony_ci	iounmap(dev->regs);
104062306a36Sopenharmony_cierr_free_resource:
104162306a36Sopenharmony_ci	pci_release_regions(pdev);
104262306a36Sopenharmony_cierr_disable_pdev:
104362306a36Sopenharmony_ci	pci_disable_device(pdev);
104462306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
104562306a36Sopenharmony_cierr_free_device:
104662306a36Sopenharmony_ci	mutex_lock(&pvrdma_device_list_lock);
104762306a36Sopenharmony_ci	list_del(&dev->device_link);
104862306a36Sopenharmony_ci	mutex_unlock(&pvrdma_device_list_lock);
104962306a36Sopenharmony_ci	ib_dealloc_device(&dev->ib_dev);
105062306a36Sopenharmony_ci	return ret;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic void pvrdma_pci_remove(struct pci_dev *pdev)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct pvrdma_dev *dev = pci_get_drvdata(pdev);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (!dev)
105862306a36Sopenharmony_ci		return;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	dev_info(&pdev->dev, "detaching from device\n");
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	unregister_netdevice_notifier(&dev->nb_netdev);
106362306a36Sopenharmony_ci	dev->nb_netdev.notifier_call = NULL;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	flush_workqueue(event_wq);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	if (dev->netdev) {
106862306a36Sopenharmony_ci		dev_put(dev->netdev);
106962306a36Sopenharmony_ci		dev->netdev = NULL;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* Unregister ib device */
107362306a36Sopenharmony_ci	ib_unregister_device(&dev->ib_dev);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	mutex_lock(&pvrdma_device_list_lock);
107662306a36Sopenharmony_ci	list_del(&dev->device_link);
107762306a36Sopenharmony_ci	mutex_unlock(&pvrdma_device_list_lock);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	pvrdma_disable_intrs(dev);
108062306a36Sopenharmony_ci	pvrdma_free_irq(dev);
108162306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* Deactivate pvrdma device */
108462306a36Sopenharmony_ci	pvrdma_write_reg(dev, PVRDMA_REG_CTL, PVRDMA_DEVICE_CTL_RESET);
108562306a36Sopenharmony_ci	pvrdma_page_dir_cleanup(dev, &dev->cq_pdir);
108662306a36Sopenharmony_ci	pvrdma_page_dir_cleanup(dev, &dev->async_pdir);
108762306a36Sopenharmony_ci	pvrdma_free_slots(dev);
108862306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(*dev->dsr), dev->dsr,
108962306a36Sopenharmony_ci			  dev->dsrbase);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	iounmap(dev->regs);
109262306a36Sopenharmony_ci	kfree(dev->sgid_tbl);
109362306a36Sopenharmony_ci	kfree(dev->cq_tbl);
109462306a36Sopenharmony_ci	kfree(dev->srq_tbl);
109562306a36Sopenharmony_ci	kfree(dev->qp_tbl);
109662306a36Sopenharmony_ci	pvrdma_uar_table_cleanup(dev);
109762306a36Sopenharmony_ci	iounmap(dev->driver_uar.map);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	ib_dealloc_device(&dev->ib_dev);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* Free pci resources */
110262306a36Sopenharmony_ci	pci_release_regions(pdev);
110362306a36Sopenharmony_ci	pci_disable_device(pdev);
110462306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic const struct pci_device_id pvrdma_pci_table[] = {
110862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_PVRDMA), },
110962306a36Sopenharmony_ci	{ 0 },
111062306a36Sopenharmony_ci};
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pvrdma_pci_table);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic struct pci_driver pvrdma_driver = {
111562306a36Sopenharmony_ci	.name		= DRV_NAME,
111662306a36Sopenharmony_ci	.id_table	= pvrdma_pci_table,
111762306a36Sopenharmony_ci	.probe		= pvrdma_pci_probe,
111862306a36Sopenharmony_ci	.remove		= pvrdma_pci_remove,
111962306a36Sopenharmony_ci};
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic int __init pvrdma_init(void)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	int err;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	event_wq = alloc_ordered_workqueue("pvrdma_event_wq", WQ_MEM_RECLAIM);
112662306a36Sopenharmony_ci	if (!event_wq)
112762306a36Sopenharmony_ci		return -ENOMEM;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	err = pci_register_driver(&pvrdma_driver);
113062306a36Sopenharmony_ci	if (err)
113162306a36Sopenharmony_ci		destroy_workqueue(event_wq);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	return err;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic void __exit pvrdma_cleanup(void)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	pci_unregister_driver(&pvrdma_driver);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	destroy_workqueue(event_wq);
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cimodule_init(pvrdma_init);
114462306a36Sopenharmony_cimodule_exit(pvrdma_cleanup);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ciMODULE_AUTHOR("VMware, Inc");
114762306a36Sopenharmony_ciMODULE_DESCRIPTION("VMware Paravirtual RDMA driver");
114862306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
1149