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_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 280 static 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 289 int 290 mlx5_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 322 void 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 330 int 331 mlx5_fpga_tools_char_init(void) 332 { 333 334 return (0); 335 } 336 337 void 338 mlx5_fpga_tools_char_deinit(void) 339 { 340 } 341