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