162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Authors: Cheng Xu <chengyou@linux.alibaba.com> */
462306a36Sopenharmony_ci/*          Kai Shen <kaishen@linux.alibaba.com> */
562306a36Sopenharmony_ci/* Copyright (c) 2020-2022, Alibaba Group. */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <net/addrconf.h>
962306a36Sopenharmony_ci#include <rdma/erdma-abi.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "erdma.h"
1262306a36Sopenharmony_ci#include "erdma_cm.h"
1362306a36Sopenharmony_ci#include "erdma_verbs.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciMODULE_AUTHOR("Cheng Xu <chengyou@linux.alibaba.com>");
1662306a36Sopenharmony_ciMODULE_DESCRIPTION("Alibaba elasticRDMA adapter driver");
1762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int erdma_netdev_event(struct notifier_block *nb, unsigned long event,
2062306a36Sopenharmony_ci			      void *arg)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct net_device *netdev = netdev_notifier_info_to_dev(arg);
2362306a36Sopenharmony_ci	struct erdma_dev *dev = container_of(nb, struct erdma_dev, netdev_nb);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (dev->netdev == NULL || dev->netdev != netdev)
2662306a36Sopenharmony_ci		goto done;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	switch (event) {
2962306a36Sopenharmony_ci	case NETDEV_UP:
3062306a36Sopenharmony_ci		dev->state = IB_PORT_ACTIVE;
3162306a36Sopenharmony_ci		erdma_port_event(dev, IB_EVENT_PORT_ACTIVE);
3262306a36Sopenharmony_ci		break;
3362306a36Sopenharmony_ci	case NETDEV_DOWN:
3462306a36Sopenharmony_ci		dev->state = IB_PORT_DOWN;
3562306a36Sopenharmony_ci		erdma_port_event(dev, IB_EVENT_PORT_ERR);
3662306a36Sopenharmony_ci		break;
3762306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
3862306a36Sopenharmony_ci		if (dev->mtu != netdev->mtu) {
3962306a36Sopenharmony_ci			erdma_set_mtu(dev, netdev->mtu);
4062306a36Sopenharmony_ci			dev->mtu = netdev->mtu;
4162306a36Sopenharmony_ci		}
4262306a36Sopenharmony_ci		break;
4362306a36Sopenharmony_ci	case NETDEV_REGISTER:
4462306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
4562306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
4662306a36Sopenharmony_ci	case NETDEV_GOING_DOWN:
4762306a36Sopenharmony_ci	case NETDEV_CHANGE:
4862306a36Sopenharmony_ci	default:
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cidone:
5362306a36Sopenharmony_ci	return NOTIFY_OK;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int erdma_enum_and_get_netdev(struct erdma_dev *dev)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct net_device *netdev;
5962306a36Sopenharmony_ci	int ret = -EPROBE_DEFER;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Already binded to a net_device, so we skip. */
6262306a36Sopenharmony_ci	if (dev->netdev)
6362306a36Sopenharmony_ci		return 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	rtnl_lock();
6662306a36Sopenharmony_ci	for_each_netdev(&init_net, netdev) {
6762306a36Sopenharmony_ci		/*
6862306a36Sopenharmony_ci		 * In erdma, the paired netdev and ibdev should have the same
6962306a36Sopenharmony_ci		 * MAC address. erdma can get the value from its PCIe bar
7062306a36Sopenharmony_ci		 * registers. Since erdma can not get the paired netdev
7162306a36Sopenharmony_ci		 * reference directly, we do a traverse here to get the paired
7262306a36Sopenharmony_ci		 * netdev.
7362306a36Sopenharmony_ci		 */
7462306a36Sopenharmony_ci		if (ether_addr_equal_unaligned(netdev->perm_addr,
7562306a36Sopenharmony_ci					       dev->attrs.peer_addr)) {
7662306a36Sopenharmony_ci			ret = ib_device_set_netdev(&dev->ibdev, netdev, 1);
7762306a36Sopenharmony_ci			if (ret) {
7862306a36Sopenharmony_ci				rtnl_unlock();
7962306a36Sopenharmony_ci				ibdev_warn(&dev->ibdev,
8062306a36Sopenharmony_ci					   "failed (%d) to link netdev", ret);
8162306a36Sopenharmony_ci				return ret;
8262306a36Sopenharmony_ci			}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci			dev->netdev = netdev;
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	rtnl_unlock();
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int erdma_device_register(struct erdma_dev *dev)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct ib_device *ibdev = &dev->ibdev;
9762306a36Sopenharmony_ci	int ret;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ret = erdma_enum_and_get_netdev(dev);
10062306a36Sopenharmony_ci	if (ret)
10162306a36Sopenharmony_ci		return ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	dev->mtu = dev->netdev->mtu;
10462306a36Sopenharmony_ci	addrconf_addr_eui48((u8 *)&ibdev->node_guid, dev->netdev->dev_addr);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = ib_register_device(ibdev, "erdma_%d", &dev->pdev->dev);
10762306a36Sopenharmony_ci	if (ret) {
10862306a36Sopenharmony_ci		dev_err(&dev->pdev->dev,
10962306a36Sopenharmony_ci			"ib_register_device failed: ret = %d\n", ret);
11062306a36Sopenharmony_ci		return ret;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	dev->netdev_nb.notifier_call = erdma_netdev_event;
11462306a36Sopenharmony_ci	ret = register_netdevice_notifier(&dev->netdev_nb);
11562306a36Sopenharmony_ci	if (ret) {
11662306a36Sopenharmony_ci		ibdev_err(&dev->ibdev, "failed to register notifier.\n");
11762306a36Sopenharmony_ci		ib_unregister_device(ibdev);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic irqreturn_t erdma_comm_irq_handler(int irq, void *data)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct erdma_dev *dev = data;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	erdma_cmdq_completion_handler(&dev->cmdq);
12862306a36Sopenharmony_ci	erdma_aeq_event_handler(dev);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return IRQ_HANDLED;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int erdma_request_vectors(struct erdma_dev *dev)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int expect_irq_num = min(num_possible_cpus() + 1, ERDMA_NUM_MSIX_VEC);
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = pci_alloc_irq_vectors(dev->pdev, 1, expect_irq_num, PCI_IRQ_MSIX);
13962306a36Sopenharmony_ci	if (ret < 0) {
14062306a36Sopenharmony_ci		dev_err(&dev->pdev->dev, "request irq vectors failed(%d)\n",
14162306a36Sopenharmony_ci			ret);
14262306a36Sopenharmony_ci		return ret;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci	dev->attrs.irq_num = ret;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int erdma_comm_irq_init(struct erdma_dev *dev)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	snprintf(dev->comm_irq.name, ERDMA_IRQNAME_SIZE, "erdma-common@pci:%s",
15262306a36Sopenharmony_ci		 pci_name(dev->pdev));
15362306a36Sopenharmony_ci	dev->comm_irq.msix_vector =
15462306a36Sopenharmony_ci		pci_irq_vector(dev->pdev, ERDMA_MSIX_VECTOR_CMDQ);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	cpumask_set_cpu(cpumask_first(cpumask_of_pcibus(dev->pdev->bus)),
15762306a36Sopenharmony_ci			&dev->comm_irq.affinity_hint_mask);
15862306a36Sopenharmony_ci	irq_set_affinity_hint(dev->comm_irq.msix_vector,
15962306a36Sopenharmony_ci			      &dev->comm_irq.affinity_hint_mask);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return request_irq(dev->comm_irq.msix_vector, erdma_comm_irq_handler, 0,
16262306a36Sopenharmony_ci			   dev->comm_irq.name, dev);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void erdma_comm_irq_uninit(struct erdma_dev *dev)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	irq_set_affinity_hint(dev->comm_irq.msix_vector, NULL);
16862306a36Sopenharmony_ci	free_irq(dev->comm_irq.msix_vector, dev);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int erdma_device_init(struct erdma_dev *dev, struct pci_dev *pdev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	int ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&pdev->dev,
17662306a36Sopenharmony_ci					DMA_BIT_MASK(ERDMA_PCI_WIDTH));
17762306a36Sopenharmony_ci	if (ret)
17862306a36Sopenharmony_ci		return ret;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void erdma_hw_reset(struct erdma_dev *dev)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	u32 ctrl = FIELD_PREP(ERDMA_REG_DEV_CTRL_RESET_MASK, 1);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	erdma_reg_write32(dev, ERDMA_REGS_DEV_CTRL_REG, ctrl);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int erdma_wait_hw_init_done(struct erdma_dev *dev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int i;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	erdma_reg_write32(dev, ERDMA_REGS_DEV_CTRL_REG,
19762306a36Sopenharmony_ci			  FIELD_PREP(ERDMA_REG_DEV_CTRL_INIT_MASK, 1));
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	for (i = 0; i < ERDMA_WAIT_DEV_DONE_CNT; i++) {
20062306a36Sopenharmony_ci		if (erdma_reg_read32_filed(dev, ERDMA_REGS_DEV_ST_REG,
20162306a36Sopenharmony_ci					   ERDMA_REG_DEV_ST_INIT_DONE_MASK))
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		msleep(ERDMA_REG_ACCESS_WAIT_MS);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (i == ERDMA_WAIT_DEV_DONE_CNT) {
20862306a36Sopenharmony_ci		dev_err(&dev->pdev->dev, "wait init done failed.\n");
20962306a36Sopenharmony_ci		return -ETIMEDOUT;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic const struct pci_device_id erdma_pci_tbl[] = {
21662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ALIBABA, 0x107f) },
21762306a36Sopenharmony_ci	{}
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int erdma_probe_dev(struct pci_dev *pdev)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct erdma_dev *dev;
22362306a36Sopenharmony_ci	int bars, err;
22462306a36Sopenharmony_ci	u32 version;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	err = pci_enable_device(pdev);
22762306a36Sopenharmony_ci	if (err) {
22862306a36Sopenharmony_ci		dev_err(&pdev->dev, "pci_enable_device failed(%d)\n", err);
22962306a36Sopenharmony_ci		return err;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	pci_set_master(pdev);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	dev = ib_alloc_device(erdma_dev, ibdev);
23562306a36Sopenharmony_ci	if (!dev) {
23662306a36Sopenharmony_ci		dev_err(&pdev->dev, "ib_alloc_device failed\n");
23762306a36Sopenharmony_ci		err = -ENOMEM;
23862306a36Sopenharmony_ci		goto err_disable_device;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
24262306a36Sopenharmony_ci	dev->pdev = pdev;
24362306a36Sopenharmony_ci	dev->attrs.numa_node = dev_to_node(&pdev->dev);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	bars = pci_select_bars(pdev, IORESOURCE_MEM);
24662306a36Sopenharmony_ci	err = pci_request_selected_regions(pdev, bars, DRV_MODULE_NAME);
24762306a36Sopenharmony_ci	if (bars != ERDMA_BAR_MASK || err) {
24862306a36Sopenharmony_ci		err = err ? err : -EINVAL;
24962306a36Sopenharmony_ci		goto err_ib_device_release;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	dev->func_bar_addr = pci_resource_start(pdev, ERDMA_FUNC_BAR);
25362306a36Sopenharmony_ci	dev->func_bar_len = pci_resource_len(pdev, ERDMA_FUNC_BAR);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	dev->func_bar =
25662306a36Sopenharmony_ci		devm_ioremap(&pdev->dev, dev->func_bar_addr, dev->func_bar_len);
25762306a36Sopenharmony_ci	if (!dev->func_bar) {
25862306a36Sopenharmony_ci		dev_err(&pdev->dev, "devm_ioremap failed.\n");
25962306a36Sopenharmony_ci		err = -EFAULT;
26062306a36Sopenharmony_ci		goto err_release_bars;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	version = erdma_reg_read32(dev, ERDMA_REGS_VERSION_REG);
26462306a36Sopenharmony_ci	if (version == 0) {
26562306a36Sopenharmony_ci		/* we knows that it is a non-functional function. */
26662306a36Sopenharmony_ci		err = -ENODEV;
26762306a36Sopenharmony_ci		goto err_iounmap_func_bar;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	err = erdma_device_init(dev, pdev);
27162306a36Sopenharmony_ci	if (err)
27262306a36Sopenharmony_ci		goto err_iounmap_func_bar;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	err = erdma_request_vectors(dev);
27562306a36Sopenharmony_ci	if (err)
27662306a36Sopenharmony_ci		goto err_iounmap_func_bar;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	err = erdma_comm_irq_init(dev);
27962306a36Sopenharmony_ci	if (err)
28062306a36Sopenharmony_ci		goto err_free_vectors;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = erdma_aeq_init(dev);
28362306a36Sopenharmony_ci	if (err)
28462306a36Sopenharmony_ci		goto err_uninit_comm_irq;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	err = erdma_cmdq_init(dev);
28762306a36Sopenharmony_ci	if (err)
28862306a36Sopenharmony_ci		goto err_uninit_aeq;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	err = erdma_wait_hw_init_done(dev);
29162306a36Sopenharmony_ci	if (err)
29262306a36Sopenharmony_ci		goto err_uninit_cmdq;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	err = erdma_ceqs_init(dev);
29562306a36Sopenharmony_ci	if (err)
29662306a36Sopenharmony_ci		goto err_reset_hw;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	erdma_finish_cmdq_init(dev);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cierr_reset_hw:
30362306a36Sopenharmony_ci	erdma_hw_reset(dev);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cierr_uninit_cmdq:
30662306a36Sopenharmony_ci	erdma_cmdq_destroy(dev);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cierr_uninit_aeq:
30962306a36Sopenharmony_ci	erdma_aeq_destroy(dev);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cierr_uninit_comm_irq:
31262306a36Sopenharmony_ci	erdma_comm_irq_uninit(dev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cierr_free_vectors:
31562306a36Sopenharmony_ci	pci_free_irq_vectors(dev->pdev);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cierr_iounmap_func_bar:
31862306a36Sopenharmony_ci	devm_iounmap(&pdev->dev, dev->func_bar);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cierr_release_bars:
32162306a36Sopenharmony_ci	pci_release_selected_regions(pdev, bars);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cierr_ib_device_release:
32462306a36Sopenharmony_ci	ib_dealloc_device(&dev->ibdev);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cierr_disable_device:
32762306a36Sopenharmony_ci	pci_disable_device(pdev);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return err;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void erdma_remove_dev(struct pci_dev *pdev)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct erdma_dev *dev = pci_get_drvdata(pdev);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	erdma_ceqs_uninit(dev);
33762306a36Sopenharmony_ci	erdma_hw_reset(dev);
33862306a36Sopenharmony_ci	erdma_cmdq_destroy(dev);
33962306a36Sopenharmony_ci	erdma_aeq_destroy(dev);
34062306a36Sopenharmony_ci	erdma_comm_irq_uninit(dev);
34162306a36Sopenharmony_ci	pci_free_irq_vectors(dev->pdev);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	devm_iounmap(&pdev->dev, dev->func_bar);
34462306a36Sopenharmony_ci	pci_release_selected_regions(pdev, ERDMA_BAR_MASK);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	ib_dealloc_device(&dev->ibdev);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	pci_disable_device(pdev);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci#define ERDMA_GET_CAP(name, cap) FIELD_GET(ERDMA_CMD_DEV_CAP_##name##_MASK, cap)
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int erdma_dev_attrs_init(struct erdma_dev *dev)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int err;
35662306a36Sopenharmony_ci	u64 req_hdr, cap0, cap1;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	erdma_cmdq_build_reqhdr(&req_hdr, CMDQ_SUBMOD_RDMA,
35962306a36Sopenharmony_ci				CMDQ_OPCODE_QUERY_DEVICE);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	err = erdma_post_cmd_wait(&dev->cmdq, &req_hdr, sizeof(req_hdr), &cap0,
36262306a36Sopenharmony_ci				  &cap1);
36362306a36Sopenharmony_ci	if (err)
36462306a36Sopenharmony_ci		return err;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	dev->attrs.max_cqe = 1 << ERDMA_GET_CAP(MAX_CQE, cap0);
36762306a36Sopenharmony_ci	dev->attrs.max_mr_size = 1ULL << ERDMA_GET_CAP(MAX_MR_SIZE, cap0);
36862306a36Sopenharmony_ci	dev->attrs.max_mw = 1 << ERDMA_GET_CAP(MAX_MW, cap1);
36962306a36Sopenharmony_ci	dev->attrs.max_recv_wr = 1 << ERDMA_GET_CAP(MAX_RECV_WR, cap0);
37062306a36Sopenharmony_ci	dev->attrs.local_dma_key = ERDMA_GET_CAP(DMA_LOCAL_KEY, cap1);
37162306a36Sopenharmony_ci	dev->attrs.cc = ERDMA_GET_CAP(DEFAULT_CC, cap1);
37262306a36Sopenharmony_ci	dev->attrs.max_qp = ERDMA_NQP_PER_QBLOCK * ERDMA_GET_CAP(QBLOCK, cap1);
37362306a36Sopenharmony_ci	dev->attrs.max_mr = dev->attrs.max_qp << 1;
37462306a36Sopenharmony_ci	dev->attrs.max_cq = dev->attrs.max_qp << 1;
37562306a36Sopenharmony_ci	dev->attrs.cap_flags = ERDMA_GET_CAP(FLAGS, cap0);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	dev->attrs.max_send_wr = ERDMA_MAX_SEND_WR;
37862306a36Sopenharmony_ci	dev->attrs.max_ord = ERDMA_MAX_ORD;
37962306a36Sopenharmony_ci	dev->attrs.max_ird = ERDMA_MAX_IRD;
38062306a36Sopenharmony_ci	dev->attrs.max_send_sge = ERDMA_MAX_SEND_SGE;
38162306a36Sopenharmony_ci	dev->attrs.max_recv_sge = ERDMA_MAX_RECV_SGE;
38262306a36Sopenharmony_ci	dev->attrs.max_sge_rd = ERDMA_MAX_SGE_RD;
38362306a36Sopenharmony_ci	dev->attrs.max_pd = ERDMA_MAX_PD;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	dev->res_cb[ERDMA_RES_TYPE_PD].max_cap = ERDMA_MAX_PD;
38662306a36Sopenharmony_ci	dev->res_cb[ERDMA_RES_TYPE_STAG_IDX].max_cap = dev->attrs.max_mr;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	erdma_cmdq_build_reqhdr(&req_hdr, CMDQ_SUBMOD_COMMON,
38962306a36Sopenharmony_ci				CMDQ_OPCODE_QUERY_FW_INFO);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	err = erdma_post_cmd_wait(&dev->cmdq, &req_hdr, sizeof(req_hdr), &cap0,
39262306a36Sopenharmony_ci				  &cap1);
39362306a36Sopenharmony_ci	if (!err)
39462306a36Sopenharmony_ci		dev->attrs.fw_version =
39562306a36Sopenharmony_ci			FIELD_GET(ERDMA_CMD_INFO0_FW_VER_MASK, cap0);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return err;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int erdma_device_config(struct erdma_dev *dev)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct erdma_cmdq_config_device_req req = {};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (!(dev->attrs.cap_flags & ERDMA_DEV_CAP_FLAGS_EXTEND_DB))
40562306a36Sopenharmony_ci		return 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_COMMON,
40862306a36Sopenharmony_ci				CMDQ_OPCODE_CONF_DEVICE);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	req.cfg = FIELD_PREP(ERDMA_CMD_CONFIG_DEVICE_PGSHIFT_MASK, PAGE_SHIFT) |
41162306a36Sopenharmony_ci		  FIELD_PREP(ERDMA_CMD_CONFIG_DEVICE_PS_EN_MASK, 1);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int erdma_res_cb_init(struct erdma_dev *dev)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	int i, j;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	for (i = 0; i < ERDMA_RES_CNT; i++) {
42162306a36Sopenharmony_ci		dev->res_cb[i].next_alloc_idx = 1;
42262306a36Sopenharmony_ci		spin_lock_init(&dev->res_cb[i].lock);
42362306a36Sopenharmony_ci		dev->res_cb[i].bitmap =
42462306a36Sopenharmony_ci			bitmap_zalloc(dev->res_cb[i].max_cap, GFP_KERNEL);
42562306a36Sopenharmony_ci		if (!dev->res_cb[i].bitmap)
42662306a36Sopenharmony_ci			goto err;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cierr:
43262306a36Sopenharmony_ci	for (j = 0; j < i; j++)
43362306a36Sopenharmony_ci		bitmap_free(dev->res_cb[j].bitmap);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return -ENOMEM;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void erdma_res_cb_free(struct erdma_dev *dev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	int i;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	for (i = 0; i < ERDMA_RES_CNT; i++)
44362306a36Sopenharmony_ci		bitmap_free(dev->res_cb[i].bitmap);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic const struct ib_device_ops erdma_device_ops = {
44762306a36Sopenharmony_ci	.owner = THIS_MODULE,
44862306a36Sopenharmony_ci	.driver_id = RDMA_DRIVER_ERDMA,
44962306a36Sopenharmony_ci	.uverbs_abi_ver = ERDMA_ABI_VERSION,
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	.alloc_mr = erdma_ib_alloc_mr,
45262306a36Sopenharmony_ci	.alloc_pd = erdma_alloc_pd,
45362306a36Sopenharmony_ci	.alloc_ucontext = erdma_alloc_ucontext,
45462306a36Sopenharmony_ci	.create_cq = erdma_create_cq,
45562306a36Sopenharmony_ci	.create_qp = erdma_create_qp,
45662306a36Sopenharmony_ci	.dealloc_pd = erdma_dealloc_pd,
45762306a36Sopenharmony_ci	.dealloc_ucontext = erdma_dealloc_ucontext,
45862306a36Sopenharmony_ci	.dereg_mr = erdma_dereg_mr,
45962306a36Sopenharmony_ci	.destroy_cq = erdma_destroy_cq,
46062306a36Sopenharmony_ci	.destroy_qp = erdma_destroy_qp,
46162306a36Sopenharmony_ci	.get_dma_mr = erdma_get_dma_mr,
46262306a36Sopenharmony_ci	.get_port_immutable = erdma_get_port_immutable,
46362306a36Sopenharmony_ci	.iw_accept = erdma_accept,
46462306a36Sopenharmony_ci	.iw_add_ref = erdma_qp_get_ref,
46562306a36Sopenharmony_ci	.iw_connect = erdma_connect,
46662306a36Sopenharmony_ci	.iw_create_listen = erdma_create_listen,
46762306a36Sopenharmony_ci	.iw_destroy_listen = erdma_destroy_listen,
46862306a36Sopenharmony_ci	.iw_get_qp = erdma_get_ibqp,
46962306a36Sopenharmony_ci	.iw_reject = erdma_reject,
47062306a36Sopenharmony_ci	.iw_rem_ref = erdma_qp_put_ref,
47162306a36Sopenharmony_ci	.map_mr_sg = erdma_map_mr_sg,
47262306a36Sopenharmony_ci	.mmap = erdma_mmap,
47362306a36Sopenharmony_ci	.mmap_free = erdma_mmap_free,
47462306a36Sopenharmony_ci	.modify_qp = erdma_modify_qp,
47562306a36Sopenharmony_ci	.post_recv = erdma_post_recv,
47662306a36Sopenharmony_ci	.post_send = erdma_post_send,
47762306a36Sopenharmony_ci	.poll_cq = erdma_poll_cq,
47862306a36Sopenharmony_ci	.query_device = erdma_query_device,
47962306a36Sopenharmony_ci	.query_gid = erdma_query_gid,
48062306a36Sopenharmony_ci	.query_port = erdma_query_port,
48162306a36Sopenharmony_ci	.query_qp = erdma_query_qp,
48262306a36Sopenharmony_ci	.req_notify_cq = erdma_req_notify_cq,
48362306a36Sopenharmony_ci	.reg_user_mr = erdma_reg_user_mr,
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_cq, erdma_cq, ibcq),
48662306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_pd, erdma_pd, ibpd),
48762306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_ucontext, erdma_ucontext, ibucontext),
48862306a36Sopenharmony_ci	INIT_RDMA_OBJ_SIZE(ib_qp, erdma_qp, ibqp),
48962306a36Sopenharmony_ci};
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int erdma_ib_device_add(struct pci_dev *pdev)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct erdma_dev *dev = pci_get_drvdata(pdev);
49462306a36Sopenharmony_ci	struct ib_device *ibdev = &dev->ibdev;
49562306a36Sopenharmony_ci	u64 mac;
49662306a36Sopenharmony_ci	int ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	ret = erdma_dev_attrs_init(dev);
49962306a36Sopenharmony_ci	if (ret)
50062306a36Sopenharmony_ci		return ret;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	ret = erdma_device_config(dev);
50362306a36Sopenharmony_ci	if (ret)
50462306a36Sopenharmony_ci		return ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ibdev->node_type = RDMA_NODE_RNIC;
50762306a36Sopenharmony_ci	memcpy(ibdev->node_desc, ERDMA_NODE_DESC, sizeof(ERDMA_NODE_DESC));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/*
51062306a36Sopenharmony_ci	 * Current model (one-to-one device association):
51162306a36Sopenharmony_ci	 * One ERDMA device per net_device or, equivalently,
51262306a36Sopenharmony_ci	 * per physical port.
51362306a36Sopenharmony_ci	 */
51462306a36Sopenharmony_ci	ibdev->phys_port_cnt = 1;
51562306a36Sopenharmony_ci	ibdev->num_comp_vectors = dev->attrs.irq_num - 1;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ib_set_device_ops(ibdev, &erdma_device_ops);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->cep_list);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	spin_lock_init(&dev->lock);
52262306a36Sopenharmony_ci	xa_init_flags(&dev->qp_xa, XA_FLAGS_ALLOC1);
52362306a36Sopenharmony_ci	xa_init_flags(&dev->cq_xa, XA_FLAGS_ALLOC1);
52462306a36Sopenharmony_ci	dev->next_alloc_cqn = 1;
52562306a36Sopenharmony_ci	dev->next_alloc_qpn = 1;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	ret = erdma_res_cb_init(dev);
52862306a36Sopenharmony_ci	if (ret)
52962306a36Sopenharmony_ci		return ret;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	atomic_set(&dev->num_ctx, 0);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mac = erdma_reg_read32(dev, ERDMA_REGS_NETDEV_MAC_L_REG);
53462306a36Sopenharmony_ci	mac |= (u64)erdma_reg_read32(dev, ERDMA_REGS_NETDEV_MAC_H_REG) << 32;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	u64_to_ether_addr(mac, dev->attrs.peer_addr);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	dev->reflush_wq = alloc_workqueue("erdma-reflush-wq", WQ_UNBOUND,
53962306a36Sopenharmony_ci					  WQ_UNBOUND_MAX_ACTIVE);
54062306a36Sopenharmony_ci	if (!dev->reflush_wq) {
54162306a36Sopenharmony_ci		ret = -ENOMEM;
54262306a36Sopenharmony_ci		goto err_alloc_workqueue;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ret = erdma_device_register(dev);
54662306a36Sopenharmony_ci	if (ret)
54762306a36Sopenharmony_ci		goto err_register;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cierr_register:
55262306a36Sopenharmony_ci	destroy_workqueue(dev->reflush_wq);
55362306a36Sopenharmony_cierr_alloc_workqueue:
55462306a36Sopenharmony_ci	xa_destroy(&dev->qp_xa);
55562306a36Sopenharmony_ci	xa_destroy(&dev->cq_xa);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	erdma_res_cb_free(dev);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return ret;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void erdma_ib_device_remove(struct pci_dev *pdev)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct erdma_dev *dev = pci_get_drvdata(pdev);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	unregister_netdevice_notifier(&dev->netdev_nb);
56762306a36Sopenharmony_ci	ib_unregister_device(&dev->ibdev);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	destroy_workqueue(dev->reflush_wq);
57062306a36Sopenharmony_ci	erdma_res_cb_free(dev);
57162306a36Sopenharmony_ci	xa_destroy(&dev->qp_xa);
57262306a36Sopenharmony_ci	xa_destroy(&dev->cq_xa);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int erdma_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	int ret;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ret = erdma_probe_dev(pdev);
58062306a36Sopenharmony_ci	if (ret)
58162306a36Sopenharmony_ci		return ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	ret = erdma_ib_device_add(pdev);
58462306a36Sopenharmony_ci	if (ret) {
58562306a36Sopenharmony_ci		erdma_remove_dev(pdev);
58662306a36Sopenharmony_ci		return ret;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic void erdma_remove(struct pci_dev *pdev)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	erdma_ib_device_remove(pdev);
59562306a36Sopenharmony_ci	erdma_remove_dev(pdev);
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct pci_driver erdma_pci_driver = {
59962306a36Sopenharmony_ci	.name = DRV_MODULE_NAME,
60062306a36Sopenharmony_ci	.id_table = erdma_pci_tbl,
60162306a36Sopenharmony_ci	.probe = erdma_probe,
60262306a36Sopenharmony_ci	.remove = erdma_remove
60362306a36Sopenharmony_ci};
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, erdma_pci_tbl);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic __init int erdma_init_module(void)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	int ret;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	ret = erdma_cm_init();
61262306a36Sopenharmony_ci	if (ret)
61362306a36Sopenharmony_ci		return ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	ret = pci_register_driver(&erdma_pci_driver);
61662306a36Sopenharmony_ci	if (ret)
61762306a36Sopenharmony_ci		erdma_cm_exit();
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return ret;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic void __exit erdma_exit_module(void)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	pci_unregister_driver(&erdma_pci_driver);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	erdma_cm_exit();
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cimodule_init(erdma_init_module);
63062306a36Sopenharmony_cimodule_exit(erdma_exit_module);
631