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 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/conf.h>
36 #include <dev/mlx5/mlx5io.h>
37 #include <dev/mlx5/mlx5_fpga_tools/tools_char.h>
38 
39 #define CHUNK_SIZE (128 * 1024)
40 
41 struct tools_context {
42 	struct mlx5_fpga_tools_dev *tdev;
43 	enum mlx5_fpga_access_type access_type;
44 };
45 
46 static void
47 tools_char_ctx_dtor(void *data)
48 {
49 
50 	free(data, M_DEVBUF);
51 }
52 
53 static int
54 tools_char_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
55 {
56 	struct tools_context *context;
57 
58 	context = malloc(sizeof(*context), M_DEVBUF, M_WAITOK);
59 	context->tdev = dev->si_drv1;
60 	context->access_type = MLX5_FPGA_ACCESS_TYPE_DONTCARE;
61 	devfs_set_cdevpriv(context, tools_char_ctx_dtor);
62 	return (0);
63 }
64 
65 static int
66 mem_read(struct mlx5_fpga_tools_dev *tdev, void *buf, size_t count,
67     u64 address, enum mlx5_fpga_access_type access_type, size_t *retcnt)
68 {
69 	int ret;
70 
71 	ret = sx_xlock_sig(&tdev->lock);
72 	if (ret != 0)
73 		return (ret);
74 	ret = mlx5_fpga_mem_read(tdev->fdev, count, address, buf, access_type);
75 	sx_xunlock(&tdev->lock);
76 	if (ret < 0) {
77 		dev_dbg(mlx5_fpga_dev(tdev->fdev),
78 			"Failed to read %zu bytes at address 0x%jx: %d\n",
79 			count, (uintmax_t)address, ret);
80 		return (-ret);
81 	}
82 	*retcnt = ret;
83 	return (0);
84 }
85 
86 static int
87 mem_write(struct mlx5_fpga_tools_dev *tdev, void *buf, size_t count,
88     u64 address, enum mlx5_fpga_access_type access_type, size_t *retcnt)
89 {
90 	int ret;
91 
92 	ret = sx_xlock_sig(&tdev->lock);
93 	if (ret != 0)
94 		return (ret);
95 	ret = mlx5_fpga_mem_write(tdev->fdev, count, address, buf, access_type);
96 	sx_xunlock(&tdev->lock);
97 	if (ret < 0) {
98 		dev_dbg(mlx5_fpga_dev(tdev->fdev),
99 			"Failed to write %zu bytes at address 0x%jx: %d\n",
100 			count, (uintmax_t)address, ret);
101 		return (-ret);
102 	}
103 	*retcnt = ret;
104 	return (0);
105 }
106 
107 static void
108 tools_char_llseek(struct tools_context *context, struct uio *uio, ssize_t *len)
109 {
110 	uint64_t fbase, fsize;
111 	size_t llen;
112 
113 	llen = uio->uio_resid;
114 	if (llen < 1) {
115 		*len = 0;
116 		return;
117 	}
118 	if (llen > CHUNK_SIZE)
119 		llen = CHUNK_SIZE;
120 	fbase = mlx5_fpga_ddr_base_get(context->tdev->fdev);
121 	fsize = mlx5_fpga_ddr_size_get(context->tdev->fdev);
122 	if (uio->uio_offset > fbase)
123 		*len = 0;
124 	else if (uio->uio_offset + *len > fbase + fsize)
125 		*len = fbase + fsize - uio->uio_offset;
126 	else
127 		*len = llen;
128 }
129 
130 static int
131 tools_char_read(struct cdev *dev, struct uio *uio, int ioflag)
132 {
133 	struct tools_context *context;
134 	void *kbuf;
135 	size_t len, len1;
136 	int ret;
137 
138 	ret = devfs_get_cdevpriv((void **)&context);
139 	if (ret != 0)
140 		return (ret);
141 	dev_dbg(mlx5_fpga_dev(context->tdev->fdev),
142 	    "tools char device reading %zu bytes at 0x%jx\n", uio->uio_resid,
143 	     (uintmax_t)uio->uio_offset);
144 
145 	tools_char_llseek(context, uio, &len);
146 	if (len == 0)
147 		return (0);
148 
149 	kbuf = malloc(len, M_DEVBUF, M_WAITOK);
150 	ret = mem_read(context->tdev, kbuf, len, uio->uio_offset,
151 	    context->access_type, &len1);
152 	if (ret == 0)
153 		ret = uiomove(kbuf, len1, uio);
154 	free(kbuf, M_DEVBUF);
155 	return (ret);
156 }
157 
158 static int
159 tools_char_write(struct cdev *dev, struct uio *uio, int ioflag)
160 {
161 	struct tools_context *context;
162 	void *kbuf;
163 	off_t of;
164 	size_t len, len1;
165 	int ret;
166 
167 	ret = devfs_get_cdevpriv((void **)&context);
168 	if (ret != 0)
169 		return (ret);
170 	dev_dbg(mlx5_fpga_dev(context->tdev->fdev),
171 	    "tools char device reading %zu bytes at 0x%jx\n", uio->uio_resid,
172 	    (uintmax_t)uio->uio_offset);
173 
174 	tools_char_llseek(context, uio, &len);
175 	if (len == 0)
176 		return (0);
177 
178 	kbuf = malloc(len, M_DEVBUF, M_WAITOK);
179 	len1 = uio->uio_resid;
180 	of = uio->uio_offset;
181 
182 	ret = uiomove(kbuf, len, uio);
183 	if (ret == 0) {
184 		len1 -= uio->uio_resid;
185 		ret = mem_write(context->tdev, kbuf, len, of,
186 		    context->access_type, &len1);
187 	}
188 	free(kbuf, M_DEVBUF);
189 	return (ret);
190 }
191 
192 CTASSERT(MLX5_FPGA_CAP_ARR_SZ == MLX5_ST_SZ_DW(fpga_cap));
193 
194 static int
195 tools_char_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
196     struct thread *td)
197 {
198 	struct tools_context *context;
199 	struct mlx5_fpga_device *fdev;
200 	struct mlx5_fpga_query query;
201 	struct mlx5_fpga_temperature *temperature;
202 	enum mlx5_fpga_connect *connect;
203 	u32 fpga_cap[MLX5_ST_SZ_DW(fpga_cap)] = {0};
204 	int arg, err;
205 
206 	err = devfs_get_cdevpriv((void **)&context);
207 	if (err != 0)
208 		return (err);
209 	fdev = context->tdev->fdev;
210 	if (fdev == NULL)
211 		return (ENXIO);
212 
213 	switch (cmd) {
214 	case MLX5_FPGA_ACCESS_TYPE:
215 		arg = *(int *)data;
216 		if (arg > MLX5_FPGA_ACCESS_TYPE_MAX) {
217 			dev_err(mlx5_fpga_dev(fdev),
218 			    "unknown access type %u\n", arg);
219 			err = EINVAL;
220 			break;
221 		}
222 		context->access_type = arg;
223 		break;
224 	case MLX5_FPGA_LOAD:
225 		arg = *(int *)data;
226 		if (arg > MLX5_FPGA_IMAGE_FACTORY) {
227 			dev_err(mlx5_fpga_dev(fdev),
228 				"unknown image type %u\n", arg);
229 			err = EINVAL;
230 			break;
231 		}
232 		err = mlx5_fpga_device_reload(fdev, arg);
233 		break;
234 	case MLX5_FPGA_RESET:
235 		err = mlx5_fpga_device_reload(fdev, MLX5_FPGA_IMAGE_RESET);
236 		break;
237 	case MLX5_FPGA_RELOAD:
238 		err = mlx5_fpga_device_reload(fdev, MLX5_FPGA_IMAGE_RELOAD);
239 		break;
240 	case MLX5_FPGA_IMAGE_SEL:
241 		arg = *(int *)data;
242 		if (arg > MLX5_FPGA_IMAGE_FACTORY) {
243 			dev_err(mlx5_fpga_dev(fdev),
244 			    "unknown image type %u\n", arg);
245 			err = EINVAL;
246 			break;
247 		}
248 		err = mlx5_fpga_flash_select(fdev, arg);
249 		break;
250 	case MLX5_FPGA_QUERY:
251 		mlx5_fpga_device_query(fdev, &query);
252 		bcopy(&query, data, sizeof(query));
253 		err = 0;
254 		break;
255 	case MLX5_FPGA_CAP:
256 		mlx5_fpga_get_cap(fdev, fpga_cap);
257 		bcopy(&fpga_cap, data, sizeof(fpga_cap));
258 		err = 0;
259 		break;
260 	case MLX5_FPGA_TEMPERATURE:
261 		temperature = (struct mlx5_fpga_temperature *)data;
262 		mlx5_fpga_temperature(fdev, temperature);
263 		err = 0; /* XXXKIB */
264 		break;
265 	case MLX5_FPGA_CONNECT:
266 		connect = (enum mlx5_fpga_connect *)data;
267 		mlx5_fpga_connectdisconnect(fdev, connect);
268 		err = 0; /* XXXKIB */
269  		break;
270 	default:
271 		dev_err(mlx5_fpga_dev(fdev),
272 			"unknown ioctl command %#08lx\n", cmd);
273 		err = ENOTTY;
274 	}
275 	return (err);
276 }
277 
278 static struct cdevsw mlx5_tools_char_cdevsw = {
279 	.d_version =	D_VERSION,
280 	.d_name =	"mlx5_tools_char",
281 	.d_open =	tools_char_open,
282 	.d_read =	tools_char_read,
283 	.d_write =	tools_char_write,
284 	.d_ioctl =	tools_char_ioctl,
285 };
286 
287 int
288 mlx5_fpga_tools_char_add_one(struct mlx5_fpga_tools_dev *tdev)
289 {
290 	struct make_dev_args mda;
291 	struct cdev *cd;
292 	device_t bdev;
293 	int ret;
294 
295 	make_dev_args_init(&mda);
296 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
297 	mda.mda_devsw = &mlx5_tools_char_cdevsw;
298 	mda.mda_uid = UID_ROOT;
299 	mda.mda_gid = GID_OPERATOR;
300 	mda.mda_mode = 0660;
301 	mda.mda_si_drv1 = tdev;
302 	bdev = mlx5_fpga_dev(tdev->fdev)->bsddev;
303 	ret = make_dev_s(&mda, &cd,
304 	    "%04x:%02x:%02x.%x" MLX5_FPGA_TOOLS_NAME_SUFFIX,
305 	    pci_get_domain(bdev), pci_get_bus(bdev), pci_get_slot(bdev),
306 	    pci_get_function(bdev));
307 	if (ret != 0) {
308 		tdev->char_device = NULL;
309 		dev_err(mlx5_fpga_dev(tdev->fdev),
310 		    "Failed to create a char device: %d\n", ret);
311 		return (-ret);
312 	}
313 	tdev->char_device = cd;
314 
315 	dev_dbg(mlx5_fpga_dev(tdev->fdev), "tools char device %s created\n",
316 	    cd->si_name);
317 	return (0);
318 }
319 
320 void mlx5_fpga_tools_char_remove_one(struct mlx5_fpga_tools_dev *tdev)
321 {
322 
323 	dev_err(mlx5_fpga_dev(tdev->fdev), "tools char device %s destroyed\n",
324 	    ((struct cdev *)(tdev->char_device))->si_name);
325 	destroy_dev((struct cdev *)(tdev->char_device));
326 }
327 
328 int
329 mlx5_fpga_tools_char_init(void)
330 {
331 
332 	return (0);
333 }
334 
335 void
336 mlx5_fpga_tools_char_deinit(void)
337 {
338 }
339