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
41struct tools_context {
42	struct mlx5_fpga_tools_dev *tdev;
43	enum mlx5_fpga_access_type access_type;
44};
45
46static void
47tools_char_ctx_dtor(void *data)
48{
49
50	free(data, M_DEVBUF);
51}
52
53static int
54tools_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
65static int
66mem_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
86static int
87mem_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
107static void
108tools_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
130static int
131tools_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
158static int
159tools_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
192CTASSERT(MLX5_FPGA_CAP_ARR_SZ == MLX5_ST_SZ_DW(fpga_cap));
193
194static int
195tools_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
278static 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
287int
288mlx5_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
320void 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
328int
329mlx5_fpga_tools_char_init(void)
330{
331
332	return (0);
333}
334
335void
336mlx5_fpga_tools_char_deinit(void)
337{
338}
339