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
tools_char_ctx_dtor(void * data)47 tools_char_ctx_dtor(void *data)
48 {
49
50 free(data, M_DEVBUF);
51 }
52
53 static int
tools_char_open(struct cdev * dev,int oflags,int devtype,struct thread * td)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
mem_read(struct mlx5_fpga_tools_dev * tdev,void * buf,size_t count,u64 address,enum mlx5_fpga_access_type access_type,size_t * retcnt)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
mem_write(struct mlx5_fpga_tools_dev * tdev,void * buf,size_t count,u64 address,enum mlx5_fpga_access_type access_type,size_t * retcnt)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
tools_char_llseek(struct tools_context * context,struct uio * uio,ssize_t * len)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
tools_char_read(struct cdev * dev,struct uio * uio,int ioflag)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
tools_char_write(struct cdev * dev,struct uio * uio,int ioflag)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
tools_char_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)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
mlx5_fpga_tools_char_add_one(struct mlx5_fpga_tools_dev * tdev)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
mlx5_fpga_tools_char_remove_one(struct mlx5_fpga_tools_dev * tdev)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
mlx5_fpga_tools_char_init(void)329 mlx5_fpga_tools_char_init(void)
330 {
331
332 return (0);
333 }
334
335 void
mlx5_fpga_tools_char_deinit(void)336 mlx5_fpga_tools_char_deinit(void)
337 {
338 }
339