1 /*-
2  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <dev/mlx5/mlx5io.h>
39 #include <dev/mlx5/mlx5_fpga_tools/tools_char.h>
40 
41 #define CHUNK_SIZE (128 * 1024)
42 
43 struct tools_context {
44 	struct mlx5_fpga_tools_dev *tdev;
45 	enum mlx5_fpga_access_type access_type;
46 };
47 
48 static void
49 tools_char_ctx_dtor(void *data)
50 {
51 
52 	free(data, M_DEVBUF);
53 }
54 
55 static int
56 tools_char_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
57 {
58 	struct tools_context *context;
59 
60 	context = malloc(sizeof(*context), M_DEVBUF, M_WAITOK);
61 	context->tdev = dev->si_drv1;
62 	context->access_type = MLX5_FPGA_ACCESS_TYPE_DONTCARE;
63 	devfs_set_cdevpriv(context, tools_char_ctx_dtor);
64 	return (0);
65 }
66 
67 static int
68 mem_read(struct mlx5_fpga_tools_dev *tdev, void *buf, size_t count,
69     u64 address, enum mlx5_fpga_access_type access_type, size_t *retcnt)
70 {
71 	int ret;
72 
73 	ret = sx_xlock_sig(&tdev->lock);
74 	if (ret != 0)
75 		return (ret);
76 	ret = mlx5_fpga_mem_read(tdev->fdev, count, address, buf, access_type);
77 	sx_xunlock(&tdev->lock);
78 	if (ret < 0) {
79 		dev_dbg(mlx5_fpga_dev(tdev->fdev),
80 			"Failed to read %zu bytes at address 0x%jx: %d\n",
81 			count, (uintmax_t)address, ret);
82 		return (-ret);
83 	}
84 	*retcnt = ret;
85 	return (0);
86 }
87 
88 static int
89 mem_write(struct mlx5_fpga_tools_dev *tdev, void *buf, size_t count,
90     u64 address, enum mlx5_fpga_access_type access_type, size_t *retcnt)
91 {
92 	int ret;
93 
94 	ret = sx_xlock_sig(&tdev->lock);
95 	if (ret != 0)
96 		return (ret);
97 	ret = mlx5_fpga_mem_write(tdev->fdev, count, address, buf, access_type);
98 	sx_xunlock(&tdev->lock);
99 	if (ret < 0) {
100 		dev_dbg(mlx5_fpga_dev(tdev->fdev),
101 			"Failed to write %zu bytes at address 0x%jx: %d\n",
102 			count, (uintmax_t)address, ret);
103 		return (-ret);
104 	}
105 	*retcnt = ret;
106 	return (0);
107 }
108 
109 static void
110 tools_char_llseek(struct tools_context *context, struct uio *uio, ssize_t *len)
111 {
112 	uint64_t fbase, fsize;
113 	size_t llen;
114 
115 	llen = uio->uio_resid;
116 	if (llen < 1) {
117 		*len = 0;
118 		return;
119 	}
120 	if (llen > CHUNK_SIZE)
121 		llen = CHUNK_SIZE;
122 	fbase = mlx5_fpga_ddr_base_get(context->tdev->fdev);
123 	fsize = mlx5_fpga_ddr_size_get(context->tdev->fdev);
124 	if (uio->uio_offset > fbase)
125 		*len = 0;
126 	else if (uio->uio_offset + *len > fbase + fsize)
127 		*len = fbase + fsize - uio->uio_offset;
128 	else
129 		*len = llen;
130 }
131 
132 static int
133 tools_char_read(struct cdev *dev, struct uio *uio, int ioflag)
134 {
135 	struct tools_context *context;
136 	void *kbuf;
137 	size_t len, len1;
138 	int ret;
139 
140 	ret = devfs_get_cdevpriv((void **)&context);
141 	if (ret != 0)
142 		return (ret);
143 	dev_dbg(mlx5_fpga_dev(context->tdev->fdev),
144 	    "tools char device reading %zu bytes at 0x%jx\n", uio->uio_resid,
145 	     (uintmax_t)uio->uio_offset);
146 
147 	tools_char_llseek(context, uio, &len);
148 	if (len == 0)
149 		return (0);
150 
151 	kbuf = malloc(len, M_DEVBUF, M_WAITOK);
152 	ret = mem_read(context->tdev, kbuf, len, uio->uio_offset,
153 	    context->access_type, &len1);
154 	if (ret == 0)
155 		ret = uiomove(kbuf, len1, uio);
156 	free(kbuf, M_DEVBUF);
157 	return (ret);
158 }
159 
160 static int
161 tools_char_write(struct cdev *dev, struct uio *uio, int ioflag)
162 {
163 	struct tools_context *context;
164 	void *kbuf;
165 	off_t of;
166 	size_t len, len1;
167 	int ret;
168 
169 	ret = devfs_get_cdevpriv((void **)&context);
170 	if (ret != 0)
171 		return (ret);
172 	dev_dbg(mlx5_fpga_dev(context->tdev->fdev),
173 	    "tools char device reading %zu bytes at 0x%jx\n", uio->uio_resid,
174 	    (uintmax_t)uio->uio_offset);
175 
176 	tools_char_llseek(context, uio, &len);
177 	if (len == 0)
178 		return (0);
179 
180 	kbuf = malloc(len, M_DEVBUF, M_WAITOK);
181 	len1 = uio->uio_resid;
182 	of = uio->uio_offset;
183 
184 	ret = uiomove(kbuf, len, uio);
185 	if (ret == 0) {
186 		len1 -= uio->uio_resid;
187 		ret = mem_write(context->tdev, kbuf, len, of,
188 		    context->access_type, &len1);
189 	}
190 	free(kbuf, M_DEVBUF);
191 	return (ret);
192 }
193 
194 CTASSERT(MLX5_FPGA_CAP_ARR_SZ == MLX5_ST_SZ_DW(fpga_cap));
195 
196 static int
197 tools_char_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
198     struct thread *td)
199 {
200 	struct tools_context *context;
201 	struct mlx5_fpga_device *fdev;
202 	struct mlx5_fpga_query query;
203 	struct mlx5_fpga_temperature *temperature;
204 	enum mlx5_fpga_connect *connect;
205 	u32 fpga_cap[MLX5_ST_SZ_DW(fpga_cap)] = {0};
206 	int arg, err;
207 
208 	err = devfs_get_cdevpriv((void **)&context);
209 	if (err != 0)
210 		return (err);
211 	fdev = context->tdev->fdev;
212 	if (fdev == NULL)
213 		return (ENXIO);
214 
215 	switch (cmd) {
216 	case MLX5_FPGA_ACCESS_TYPE:
217 		arg = *(int *)data;
218 		if (arg > MLX5_FPGA_ACCESS_TYPE_MAX) {
219 			dev_err(mlx5_fpga_dev(fdev),
220 			    "unknown access type %u\n", arg);
221 			err = EINVAL;
222 			break;
223 		}
224 		context->access_type = arg;
225 		break;
226 	case MLX5_FPGA_LOAD:
227 		arg = *(int *)data;
228 		if (arg > MLX5_FPGA_IMAGE_MAX) {
229 			dev_err(mlx5_fpga_dev(fdev),
230 				"unknown image type %u\n", arg);
231 			err = EINVAL;
232 			break;
233 		}
234 		err = mlx5_fpga_device_reload(fdev, arg);
235 		break;
236 	case MLX5_FPGA_RESET:
237 		err = mlx5_fpga_device_reload(fdev, MLX5_FPGA_IMAGE_MAX + 1);
238 		break;
239 	case MLX5_FPGA_IMAGE_SEL:
240 		arg = *(int *)data;
241 		if (arg > MLX5_FPGA_IMAGE_MAX) {
242 			dev_err(mlx5_fpga_dev(fdev),
243 			    "unknown image type %u\n", arg);
244 			err = EINVAL;
245 			break;
246 		}
247 		err = mlx5_fpga_flash_select(fdev, arg);
248 		break;
249 	case MLX5_FPGA_QUERY:
250 		mlx5_fpga_device_query(fdev, &query);
251 		bcopy(&query, data, sizeof(query));
252 		err = 0;
253 		break;
254 	case MLX5_FPGA_CAP:
255 		mlx5_fpga_get_cap(fdev, fpga_cap);
256 		bcopy(&fpga_cap, data, sizeof(fpga_cap));
257 		err = 0;
258 		break;
259 	case MLX5_FPGA_TEMPERATURE:
260 		temperature = (struct mlx5_fpga_temperature *)data;
261 		mlx5_fpga_temperature(fdev, temperature);
262 		err = 0; /* XXXKIB */
263 		break;
264 	case MLX5_FPGA_CONNECT:
265 		connect = (enum mlx5_fpga_connect *)data;
266 		mlx5_fpga_connectdisconnect(fdev, connect);
267 		err = 0; /* XXXKIB */
268  		break;
269 	default:
270 		dev_err(mlx5_fpga_dev(fdev),
271 			"unknown ioctl command %#08lx\n", cmd);
272 		err = ENOTTY;
273 	}
274 	return (err);
275 }
276 
277 static struct cdevsw mlx5_tools_char_cdevsw = {
278 	.d_version =	D_VERSION,
279 	.d_name =	"mlx5_tools_char",
280 	.d_open =	tools_char_open,
281 	.d_read =	tools_char_read,
282 	.d_write =	tools_char_write,
283 	.d_ioctl =	tools_char_ioctl,
284 };
285 
286 int
287 mlx5_fpga_tools_char_add_one(struct mlx5_fpga_tools_dev *tdev)
288 {
289 	struct make_dev_args mda;
290 	struct cdev *cd;
291 	device_t bdev;
292 	int ret;
293 
294 	make_dev_args_init(&mda);
295 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
296 	mda.mda_devsw = &mlx5_tools_char_cdevsw;
297 	mda.mda_uid = UID_ROOT;
298 	mda.mda_gid = GID_OPERATOR;
299 	mda.mda_mode = 0660;
300 	mda.mda_si_drv1 = tdev;
301 	bdev = mlx5_fpga_dev(tdev->fdev)->bsddev;
302 	ret = make_dev_s(&mda, &cd,
303 	    "%04x:%02x:%02x.%x" MLX5_FPGA_TOOLS_NAME_SUFFIX,
304 	    pci_get_domain(bdev), pci_get_bus(bdev), pci_get_slot(bdev),
305 	    pci_get_function(bdev));
306 	if (ret != 0) {
307 		tdev->char_device = NULL;
308 		dev_err(mlx5_fpga_dev(tdev->fdev),
309 		    "Failed to create a char device: %d\n", ret);
310 		return (-ret);
311 	}
312 	tdev->char_device = cd;
313 
314 	dev_dbg(mlx5_fpga_dev(tdev->fdev), "tools char device %s created\n",
315 	    cd->si_name);
316 	return (0);
317 }
318 
319 void mlx5_fpga_tools_char_remove_one(struct mlx5_fpga_tools_dev *tdev)
320 {
321 
322 	dev_err(mlx5_fpga_dev(tdev->fdev), "tools char device %s destroyed\n",
323 	    ((struct cdev *)(tdev->char_device))->si_name);
324 	destroy_dev((struct cdev *)(tdev->char_device));
325 }
326 
327 int
328 mlx5_fpga_tools_char_init(void)
329 {
330 
331 	return (0);
332 }
333 
334 void
335 mlx5_fpga_tools_char_deinit(void)
336 {
337 }
338