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
43struct tools_context {
44	struct mlx5_fpga_tools_dev *tdev;
45	enum mlx5_fpga_access_type access_type;
46};
47
48static void
49tools_char_ctx_dtor(void *data)
50{
51
52	free(data, M_DEVBUF);
53}
54
55static int
56tools_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
67static int
68mem_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
88static int
89mem_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
109static void
110tools_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
132static int
133tools_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
160static int
161tools_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
194CTASSERT(MLX5_FPGA_CAP_ARR_SZ == MLX5_ST_SZ_DW(fpga_cap));
195
196static int
197tools_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_FACTORY) {
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_RESET);
238		break;
239	case MLX5_FPGA_RELOAD:
240		err = mlx5_fpga_device_reload(fdev, MLX5_FPGA_IMAGE_RELOAD);
241		break;
242	case MLX5_FPGA_IMAGE_SEL:
243		arg = *(int *)data;
244		if (arg > MLX5_FPGA_IMAGE_FACTORY) {
245			dev_err(mlx5_fpga_dev(fdev),
246			    "unknown image type %u\n", arg);
247			err = EINVAL;
248			break;
249		}
250		err = mlx5_fpga_flash_select(fdev, arg);
251		break;
252	case MLX5_FPGA_QUERY:
253		mlx5_fpga_device_query(fdev, &query);
254		bcopy(&query, data, sizeof(query));
255		err = 0;
256		break;
257	case MLX5_FPGA_CAP:
258		mlx5_fpga_get_cap(fdev, fpga_cap);
259		bcopy(&fpga_cap, data, sizeof(fpga_cap));
260		err = 0;
261		break;
262	case MLX5_FPGA_TEMPERATURE:
263		temperature = (struct mlx5_fpga_temperature *)data;
264		mlx5_fpga_temperature(fdev, temperature);
265		err = 0; /* XXXKIB */
266		break;
267	case MLX5_FPGA_CONNECT:
268		connect = (enum mlx5_fpga_connect *)data;
269		mlx5_fpga_connectdisconnect(fdev, connect);
270		err = 0; /* XXXKIB */
271 		break;
272	default:
273		dev_err(mlx5_fpga_dev(fdev),
274			"unknown ioctl command %#08lx\n", cmd);
275		err = ENOTTY;
276	}
277	return (err);
278}
279
280static struct cdevsw mlx5_tools_char_cdevsw = {
281	.d_version =	D_VERSION,
282	.d_name =	"mlx5_tools_char",
283	.d_open =	tools_char_open,
284	.d_read =	tools_char_read,
285	.d_write =	tools_char_write,
286	.d_ioctl =	tools_char_ioctl,
287};
288
289int
290mlx5_fpga_tools_char_add_one(struct mlx5_fpga_tools_dev *tdev)
291{
292	struct make_dev_args mda;
293	struct cdev *cd;
294	device_t bdev;
295	int ret;
296
297	make_dev_args_init(&mda);
298	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
299	mda.mda_devsw = &mlx5_tools_char_cdevsw;
300	mda.mda_uid = UID_ROOT;
301	mda.mda_gid = GID_OPERATOR;
302	mda.mda_mode = 0660;
303	mda.mda_si_drv1 = tdev;
304	bdev = mlx5_fpga_dev(tdev->fdev)->bsddev;
305	ret = make_dev_s(&mda, &cd,
306	    "%04x:%02x:%02x.%x" MLX5_FPGA_TOOLS_NAME_SUFFIX,
307	    pci_get_domain(bdev), pci_get_bus(bdev), pci_get_slot(bdev),
308	    pci_get_function(bdev));
309	if (ret != 0) {
310		tdev->char_device = NULL;
311		dev_err(mlx5_fpga_dev(tdev->fdev),
312		    "Failed to create a char device: %d\n", ret);
313		return (-ret);
314	}
315	tdev->char_device = cd;
316
317	dev_dbg(mlx5_fpga_dev(tdev->fdev), "tools char device %s created\n",
318	    cd->si_name);
319	return (0);
320}
321
322void mlx5_fpga_tools_char_remove_one(struct mlx5_fpga_tools_dev *tdev)
323{
324
325	dev_err(mlx5_fpga_dev(tdev->fdev), "tools char device %s destroyed\n",
326	    ((struct cdev *)(tdev->char_device))->si_name);
327	destroy_dev((struct cdev *)(tdev->char_device));
328}
329
330int
331mlx5_fpga_tools_char_init(void)
332{
333
334	return (0);
335}
336
337void
338mlx5_fpga_tools_char_deinit(void)
339{
340}
341