1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
3219820Sjeff * Copyright (c) 2006 Cisco Systems.  All rights reserved.
4219820Sjeff *
5219820Sjeff * This software is available to you under a choice of one of two
6219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
7219820Sjeff * General Public License (GPL) Version 2, available from the file
8219820Sjeff * COPYING in the main directory of this source tree, or the
9219820Sjeff * OpenIB.org BSD license below:
10219820Sjeff *
11219820Sjeff *     Redistribution and use in source and binary forms, with or
12219820Sjeff *     without modification, are permitted provided that the following
13219820Sjeff *     conditions are met:
14219820Sjeff *
15219820Sjeff *      - Redistributions of source code must retain the above
16219820Sjeff *        copyright notice, this list of conditions and the following
17219820Sjeff *        disclaimer.
18219820Sjeff *
19219820Sjeff *      - Redistributions in binary form must reproduce the above
20219820Sjeff *        copyright notice, this list of conditions and the following
21219820Sjeff *        disclaimer in the documentation and/or other materials
22219820Sjeff *        provided with the distribution.
23219820Sjeff *
24219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31219820Sjeff * SOFTWARE.
32219820Sjeff */
33219820Sjeff
34219820Sjeff#if HAVE_CONFIG_H
35219820Sjeff#  include <config.h>
36219820Sjeff#endif /* HAVE_CONFIG_H */
37219820Sjeff
38219820Sjeff#include <stdio.h>
39219820Sjeff#include <stdlib.h>
40219820Sjeff#include <unistd.h>
41219820Sjeff#include <errno.h>
42219820Sjeff#include <sys/mman.h>
43219820Sjeff#include <pthread.h>
44219820Sjeff#include <string.h>
45219820Sjeff
46219820Sjeff#ifndef HAVE_IBV_REGISTER_DRIVER
47219820Sjeff#include <sysfs/libsysfs.h>
48219820Sjeff#endif
49219820Sjeff
50219820Sjeff#ifndef HAVE_IBV_READ_SYSFS_FILE
51219820Sjeff#include <sys/types.h>
52219820Sjeff#include <sys/stat.h>
53219820Sjeff#include <fcntl.h>
54219820Sjeff#endif
55219820Sjeff
56219820Sjeff#include "mthca.h"
57219820Sjeff#include "mthca-abi.h"
58219820Sjeff
59219820Sjeff#ifndef PCI_VENDOR_ID_MELLANOX
60219820Sjeff#define PCI_VENDOR_ID_MELLANOX			0x15b3
61219820Sjeff#endif
62219820Sjeff
63219820Sjeff#ifndef PCI_DEVICE_ID_MELLANOX_TAVOR
64219820Sjeff#define PCI_DEVICE_ID_MELLANOX_TAVOR		0x5a44
65219820Sjeff#endif
66219820Sjeff
67219820Sjeff#ifndef PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT
68219820Sjeff#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT	0x6278
69219820Sjeff#endif
70219820Sjeff
71219820Sjeff#ifndef PCI_DEVICE_ID_MELLANOX_ARBEL
72219820Sjeff#define PCI_DEVICE_ID_MELLANOX_ARBEL		0x6282
73219820Sjeff#endif
74219820Sjeff
75219820Sjeff#ifndef PCI_DEVICE_ID_MELLANOX_SINAI_OLD
76219820Sjeff#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD	0x5e8c
77219820Sjeff#endif
78219820Sjeff
79219820Sjeff#ifndef PCI_DEVICE_ID_MELLANOX_SINAI
80219820Sjeff#define PCI_DEVICE_ID_MELLANOX_SINAI		0x6274
81219820Sjeff#endif
82219820Sjeff
83219820Sjeff#ifndef PCI_VENDOR_ID_TOPSPIN
84219820Sjeff#define PCI_VENDOR_ID_TOPSPIN			0x1867
85219820Sjeff#endif
86219820Sjeff
87219820Sjeff#define HCA(v, d, t) \
88219820Sjeff	{ .vendor = PCI_VENDOR_ID_##v,			\
89219820Sjeff	  .device = PCI_DEVICE_ID_MELLANOX_##d,		\
90219820Sjeff	  .type = MTHCA_##t }
91219820Sjeff
92219820Sjeffstruct {
93219820Sjeff	unsigned		vendor;
94219820Sjeff	unsigned		device;
95219820Sjeff	enum mthca_hca_type	type;
96219820Sjeff} hca_table[] = {
97219820Sjeff	HCA(MELLANOX, TAVOR,	    TAVOR),
98219820Sjeff	HCA(MELLANOX, ARBEL_COMPAT, TAVOR),
99219820Sjeff	HCA(MELLANOX, ARBEL,	    ARBEL),
100219820Sjeff	HCA(MELLANOX, SINAI_OLD,    ARBEL),
101219820Sjeff	HCA(MELLANOX, SINAI,	    ARBEL),
102219820Sjeff	HCA(TOPSPIN,  TAVOR,	    TAVOR),
103219820Sjeff	HCA(TOPSPIN,  ARBEL_COMPAT, TAVOR),
104219820Sjeff	HCA(TOPSPIN,  ARBEL,	    ARBEL),
105219820Sjeff	HCA(TOPSPIN,  SINAI_OLD,    ARBEL),
106219820Sjeff	HCA(TOPSPIN,  SINAI,	    ARBEL),
107219820Sjeff};
108219820Sjeff
109219820Sjeffstatic struct ibv_context_ops mthca_ctx_ops = {
110219820Sjeff	.query_device  = mthca_query_device,
111219820Sjeff	.query_port    = mthca_query_port,
112219820Sjeff	.alloc_pd      = mthca_alloc_pd,
113219820Sjeff	.dealloc_pd    = mthca_free_pd,
114219820Sjeff	.reg_mr        = mthca_reg_mr,
115219820Sjeff	.dereg_mr      = mthca_dereg_mr,
116219820Sjeff	.create_cq     = mthca_create_cq,
117219820Sjeff	.poll_cq       = mthca_poll_cq,
118219820Sjeff	.resize_cq     = mthca_resize_cq,
119219820Sjeff	.destroy_cq    = mthca_destroy_cq,
120219820Sjeff	.create_srq    = mthca_create_srq,
121219820Sjeff	.modify_srq    = mthca_modify_srq,
122219820Sjeff	.query_srq     = mthca_query_srq,
123219820Sjeff	.destroy_srq   = mthca_destroy_srq,
124219820Sjeff	.create_qp     = mthca_create_qp,
125219820Sjeff	.query_qp      = mthca_query_qp,
126219820Sjeff	.modify_qp     = mthca_modify_qp,
127219820Sjeff	.destroy_qp    = mthca_destroy_qp,
128219820Sjeff	.create_ah     = mthca_create_ah,
129219820Sjeff	.destroy_ah    = mthca_destroy_ah,
130219820Sjeff	.attach_mcast  = mthca_attach_mcast,
131219820Sjeff	.detach_mcast  = mthca_detach_mcast
132219820Sjeff};
133219820Sjeff
134219820Sjeffstatic struct ibv_context *mthca_alloc_context(struct ibv_device *ibdev, int cmd_fd)
135219820Sjeff{
136219820Sjeff	struct mthca_context            *context;
137219820Sjeff	struct ibv_get_context           cmd;
138219820Sjeff	struct mthca_alloc_ucontext_resp resp;
139219820Sjeff	int                              i;
140219820Sjeff
141219820Sjeff	context = calloc(1, sizeof *context);
142219820Sjeff	if (!context)
143219820Sjeff		return NULL;
144219820Sjeff
145219820Sjeff	context->ibv_ctx.cmd_fd = cmd_fd;
146219820Sjeff
147219820Sjeff	if (ibv_cmd_get_context(&context->ibv_ctx, &cmd, sizeof cmd,
148219820Sjeff				&resp.ibv_resp, sizeof resp))
149219820Sjeff		goto err_free;
150219820Sjeff
151219820Sjeff	context->num_qps        = resp.qp_tab_size;
152219820Sjeff	context->qp_table_shift = ffs(context->num_qps) - 1 - MTHCA_QP_TABLE_BITS;
153219820Sjeff	context->qp_table_mask  = (1 << context->qp_table_shift) - 1;
154219820Sjeff
155219820Sjeff	/*
156219820Sjeff	 * Need to set ibv_ctx.device because mthca_is_memfree() will
157219820Sjeff	 * look at it to figure out the HCA type.
158219820Sjeff	 */
159219820Sjeff	context->ibv_ctx.device = ibdev;
160219820Sjeff
161219820Sjeff	if (mthca_is_memfree(&context->ibv_ctx)) {
162219820Sjeff		context->db_tab = mthca_alloc_db_tab(resp.uarc_size);
163219820Sjeff		if (!context->db_tab)
164219820Sjeff			goto err_free;
165219820Sjeff	} else
166219820Sjeff		context->db_tab = NULL;
167219820Sjeff
168219820Sjeff	pthread_mutex_init(&context->qp_table_mutex, NULL);
169219820Sjeff	for (i = 0; i < MTHCA_QP_TABLE_SIZE; ++i)
170219820Sjeff		context->qp_table[i].refcnt = 0;
171219820Sjeff
172219820Sjeff	context->uar = mmap(NULL, to_mdev(ibdev)->page_size, PROT_WRITE,
173219820Sjeff			    MAP_SHARED, cmd_fd, 0);
174219820Sjeff	if (context->uar == MAP_FAILED)
175219820Sjeff		goto err_db_tab;
176219820Sjeff
177219820Sjeff	pthread_spin_init(&context->uar_lock, PTHREAD_PROCESS_PRIVATE);
178219820Sjeff
179219820Sjeff	context->pd = mthca_alloc_pd(&context->ibv_ctx);
180219820Sjeff	if (!context->pd)
181219820Sjeff		goto err_unmap;
182219820Sjeff
183219820Sjeff	context->pd->context = &context->ibv_ctx;
184219820Sjeff
185219820Sjeff	context->ibv_ctx.ops = mthca_ctx_ops;
186219820Sjeff
187219820Sjeff	if (mthca_is_memfree(&context->ibv_ctx)) {
188219820Sjeff		context->ibv_ctx.ops.req_notify_cq = mthca_arbel_arm_cq;
189219820Sjeff		context->ibv_ctx.ops.cq_event      = mthca_arbel_cq_event;
190219820Sjeff		context->ibv_ctx.ops.post_send     = mthca_arbel_post_send;
191219820Sjeff		context->ibv_ctx.ops.post_recv     = mthca_arbel_post_recv;
192219820Sjeff		context->ibv_ctx.ops.post_srq_recv = mthca_arbel_post_srq_recv;
193219820Sjeff	} else {
194219820Sjeff		context->ibv_ctx.ops.req_notify_cq = mthca_tavor_arm_cq;
195219820Sjeff		context->ibv_ctx.ops.cq_event      = NULL;
196219820Sjeff		context->ibv_ctx.ops.post_send     = mthca_tavor_post_send;
197219820Sjeff		context->ibv_ctx.ops.post_recv     = mthca_tavor_post_recv;
198219820Sjeff		context->ibv_ctx.ops.post_srq_recv = mthca_tavor_post_srq_recv;
199219820Sjeff	}
200219820Sjeff
201219820Sjeff	return &context->ibv_ctx;
202219820Sjeff
203219820Sjefferr_unmap:
204219820Sjeff	munmap(context->uar, to_mdev(ibdev)->page_size);
205219820Sjeff
206219820Sjefferr_db_tab:
207219820Sjeff	mthca_free_db_tab(context->db_tab);
208219820Sjeff
209219820Sjefferr_free:
210219820Sjeff	free(context);
211219820Sjeff	return NULL;
212219820Sjeff}
213219820Sjeff
214219820Sjeffstatic void mthca_free_context(struct ibv_context *ibctx)
215219820Sjeff{
216219820Sjeff	struct mthca_context *context = to_mctx(ibctx);
217219820Sjeff
218219820Sjeff	mthca_free_pd(context->pd);
219219820Sjeff	munmap(context->uar, to_mdev(ibctx->device)->page_size);
220219820Sjeff	mthca_free_db_tab(context->db_tab);
221219820Sjeff	free(context);
222219820Sjeff}
223219820Sjeff
224219820Sjeffstatic struct ibv_device_ops mthca_dev_ops = {
225219820Sjeff	.alloc_context = mthca_alloc_context,
226219820Sjeff	.free_context  = mthca_free_context
227219820Sjeff};
228219820Sjeff
229219820Sjeff/*
230219820Sjeff * Keep a private implementation of HAVE_IBV_READ_SYSFS_FILE to handle
231219820Sjeff * old versions of libibverbs that didn't implement it.  This can be
232219820Sjeff * removed when libibverbs 1.0.3 or newer is available "everywhere."
233219820Sjeff */
234219820Sjeff#ifndef HAVE_IBV_READ_SYSFS_FILE
235219820Sjeffstatic int ibv_read_sysfs_file(const char *dir, const char *file,
236219820Sjeff			       char *buf, size_t size)
237219820Sjeff{
238219820Sjeff	char path[256];
239219820Sjeff	int fd;
240219820Sjeff	int len;
241219820Sjeff
242219820Sjeff	snprintf(path, sizeof path, "%s/%s", dir, file);
243219820Sjeff
244219820Sjeff	fd = open(path, O_RDONLY);
245219820Sjeff	if (fd < 0)
246219820Sjeff		return -1;
247219820Sjeff
248219820Sjeff	len = read(fd, buf, size);
249219820Sjeff
250219820Sjeff	close(fd);
251219820Sjeff
252219820Sjeff	if (len > 0 && buf[len - 1] == '\n')
253219820Sjeff		buf[--len] = '\0';
254219820Sjeff
255219820Sjeff	return len;
256219820Sjeff}
257219820Sjeff#endif /* HAVE_IBV_READ_SYSFS_FILE */
258219820Sjeff
259219820Sjeffstatic struct ibv_device *mthca_driver_init(const char *uverbs_sys_path,
260219820Sjeff					    int abi_version)
261219820Sjeff{
262219820Sjeff	char			value[8];
263219820Sjeff	struct mthca_device    *dev;
264219820Sjeff	unsigned                vendor, device;
265219820Sjeff	int                     i;
266219820Sjeff
267219820Sjeff	if (ibv_read_sysfs_file(uverbs_sys_path, "device/vendor",
268219820Sjeff				value, sizeof value) < 0)
269219820Sjeff		return NULL;
270219820Sjeff	sscanf(value, "%i", &vendor);
271219820Sjeff
272219820Sjeff	if (ibv_read_sysfs_file(uverbs_sys_path, "device/device",
273219820Sjeff				value, sizeof value) < 0)
274219820Sjeff		return NULL;
275219820Sjeff	sscanf(value, "%i", &device);
276219820Sjeff
277219820Sjeff	for (i = 0; i < sizeof hca_table / sizeof hca_table[0]; ++i)
278219820Sjeff		if (vendor == hca_table[i].vendor &&
279219820Sjeff		    device == hca_table[i].device)
280219820Sjeff			goto found;
281219820Sjeff
282219820Sjeff	return NULL;
283219820Sjeff
284219820Sjefffound:
285219820Sjeff	if (abi_version > MTHCA_UVERBS_ABI_VERSION) {
286219820Sjeff		fprintf(stderr, PFX "Fatal: ABI version %d of %s is too new (expected %d)\n",
287219820Sjeff			abi_version, uverbs_sys_path, MTHCA_UVERBS_ABI_VERSION);
288219820Sjeff		return NULL;
289219820Sjeff	}
290219820Sjeff
291219820Sjeff	dev = malloc(sizeof *dev);
292219820Sjeff	if (!dev) {
293219820Sjeff		fprintf(stderr, PFX "Fatal: couldn't allocate device for %s\n",
294219820Sjeff			uverbs_sys_path);
295219820Sjeff		return NULL;
296219820Sjeff	}
297219820Sjeff
298219820Sjeff	dev->ibv_dev.ops = mthca_dev_ops;
299219820Sjeff	dev->hca_type    = hca_table[i].type;
300219820Sjeff	dev->page_size   = sysconf(_SC_PAGESIZE);
301219820Sjeff
302219820Sjeff	return &dev->ibv_dev;
303219820Sjeff}
304219820Sjeff
305219820Sjeff#ifdef HAVE_IBV_REGISTER_DRIVER
306219820Sjeffstatic __attribute__((constructor)) void mthca_register_driver(void)
307219820Sjeff{
308219820Sjeff	ibv_register_driver("mthca", mthca_driver_init);
309219820Sjeff}
310219820Sjeff#else
311219820Sjeff/*
312219820Sjeff * Export the old libsysfs sysfs_class_device-based driver entry point
313219820Sjeff * if libibverbs does not export an ibv_register_driver() function.
314219820Sjeff */
315219820Sjeffstruct ibv_device *openib_driver_init(struct sysfs_class_device *sysdev)
316219820Sjeff{
317219820Sjeff	int abi_ver = 0;
318219820Sjeff	char value[8];
319219820Sjeff
320219820Sjeff	if (ibv_read_sysfs_file(sysdev->path, "abi_version",
321219820Sjeff				value, sizeof value) > 0)
322219820Sjeff		abi_ver = strtol(value, NULL, 10);
323219820Sjeff
324219820Sjeff	return mthca_driver_init(sysdev->path, abi_ver);
325219820Sjeff}
326219820Sjeff#endif /* HAVE_IBV_REGISTER_DRIVER */
327