1 /*- 2 * Copyright (c) 2015 Brian Fundakowski Feldman. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include <sys/cdefs.h> 26 __FBSDID("$FreeBSD$"); 27 28 #include "opt_platform.h" 29 #include "opt_spi.h" 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/mman.h> 39 #include <sys/mutex.h> 40 #include <sys/module.h> 41 #include <sys/proc.h> 42 #include <sys/rwlock.h> 43 #include <sys/spigenio.h> 44 #include <sys/sysctl.h> 45 #include <sys/types.h> 46 47 #include <vm/vm.h> 48 #include <vm/vm_extern.h> 49 #include <vm/vm_object.h> 50 #include <vm/vm_page.h> 51 #include <vm/vm_pager.h> 52 53 #include <dev/spibus/spi.h> 54 #include <dev/spibus/spibusvar.h> 55 56 #ifdef FDT 57 #include <dev/ofw/ofw_bus_subr.h> 58 #endif 59 60 #include "spibus_if.h" 61 62 #define SPIGEN_OPEN (1 << 0) 63 #define SPIGEN_MMAP_BUSY (1 << 1) 64 65 struct spigen_softc { 66 device_t sc_dev; 67 struct cdev *sc_cdev; 68 #ifdef SPIGEN_LEGACY_CDEVNAME 69 struct cdev *sc_adev; /* alias device */ 70 #endif 71 struct mtx sc_mtx; 72 uint32_t sc_command_length_max; /* cannot change while mmapped */ 73 uint32_t sc_data_length_max; /* cannot change while mmapped */ 74 vm_object_t sc_mmap_buffer; /* command, then data */ 75 vm_offset_t sc_mmap_kvaddr; 76 size_t sc_mmap_buffer_size; 77 int sc_debug; 78 int sc_flags; 79 }; 80 81 static int 82 spigen_probe(device_t dev) 83 { 84 int rv; 85 86 /* 87 * By default we only bid to attach if specifically added by our parent 88 * (usually via hint.spigen.#.at=busname). On FDT systems we bid as the 89 * default driver based on being configured in the FDT data. 90 */ 91 rv = BUS_PROBE_NOWILDCARD; 92 93 #ifdef FDT 94 if (ofw_bus_status_okay(dev) && 95 ofw_bus_is_compatible(dev, "freebsd,spigen")) 96 rv = BUS_PROBE_DEFAULT; 97 #endif 98 99 device_set_desc(dev, "SPI Generic IO"); 100 101 return (rv); 102 } 103 104 static int spigen_open(struct cdev *, int, int, struct thread *); 105 static int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 106 static int spigen_close(struct cdev *, int, int, struct thread *); 107 static d_mmap_single_t spigen_mmap_single; 108 109 static struct cdevsw spigen_cdevsw = { 110 .d_version = D_VERSION, 111 .d_name = "spigen", 112 .d_open = spigen_open, 113 .d_ioctl = spigen_ioctl, 114 .d_mmap_single = spigen_mmap_single, 115 .d_close = spigen_close 116 }; 117 118 static int 119 spigen_command_length_max_proc(SYSCTL_HANDLER_ARGS) 120 { 121 struct spigen_softc *sc = (struct spigen_softc *)arg1; 122 uint32_t command_length_max; 123 int error; 124 125 mtx_lock(&sc->sc_mtx); 126 command_length_max = sc->sc_command_length_max; 127 mtx_unlock(&sc->sc_mtx); 128 error = sysctl_handle_int(oidp, &command_length_max, 129 sizeof(command_length_max), req); 130 if (error == 0 && req->newptr != NULL) { 131 mtx_lock(&sc->sc_mtx); 132 if (sc->sc_mmap_buffer != NULL) 133 error = EBUSY; 134 else 135 sc->sc_command_length_max = command_length_max; 136 mtx_unlock(&sc->sc_mtx); 137 } 138 return (error); 139 } 140 141 static int 142 spigen_data_length_max_proc(SYSCTL_HANDLER_ARGS) 143 { 144 struct spigen_softc *sc = (struct spigen_softc *)arg1; 145 uint32_t data_length_max; 146 int error; 147 148 mtx_lock(&sc->sc_mtx); 149 data_length_max = sc->sc_data_length_max; 150 mtx_unlock(&sc->sc_mtx); 151 error = sysctl_handle_int(oidp, &data_length_max, 152 sizeof(data_length_max), req); 153 if (error == 0 && req->newptr != NULL) { 154 mtx_lock(&sc->sc_mtx); 155 if (sc->sc_mmap_buffer != NULL) 156 error = EBUSY; 157 else 158 sc->sc_data_length_max = data_length_max; 159 mtx_unlock(&sc->sc_mtx); 160 } 161 return (error); 162 } 163 164 static void 165 spigen_sysctl_init(struct spigen_softc *sc) 166 { 167 struct sysctl_ctx_list *ctx; 168 struct sysctl_oid *tree_node; 169 struct sysctl_oid_list *tree; 170 171 /* 172 * Add system sysctl tree/handlers. 173 */ 174 ctx = device_get_sysctl_ctx(sc->sc_dev); 175 tree_node = device_get_sysctl_tree(sc->sc_dev); 176 tree = SYSCTL_CHILDREN(tree_node); 177 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "command_length_max", 178 CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 179 spigen_command_length_max_proc, "IU", "SPI command header portion (octets)"); 180 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "data_length_max", 181 CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 182 spigen_data_length_max_proc, "IU", "SPI data trailer portion (octets)"); 183 SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "data", CTLFLAG_RW, 184 &sc->sc_debug, 0, "debug flags"); 185 186 } 187 188 static int 189 spigen_attach(device_t dev) 190 { 191 struct spigen_softc *sc; 192 const int unit = device_get_unit(dev); 193 int cs, res; 194 struct make_dev_args mda; 195 196 spibus_get_cs(dev, &cs); 197 cs &= ~SPIBUS_CS_HIGH; /* trim 'cs high' bit */ 198 199 sc = device_get_softc(dev); 200 sc->sc_dev = dev; 201 sc->sc_command_length_max = PAGE_SIZE; 202 sc->sc_data_length_max = PAGE_SIZE; 203 204 mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 205 206 make_dev_args_init(&mda); 207 mda.mda_flags = MAKEDEV_WAITOK; 208 mda.mda_devsw = &spigen_cdevsw; 209 mda.mda_cr = NULL; 210 mda.mda_uid = UID_ROOT; 211 mda.mda_gid = GID_OPERATOR; 212 mda.mda_mode = 0660; 213 mda.mda_unit = unit; 214 mda.mda_si_drv1 = dev; 215 216 res = make_dev_s(&mda, &(sc->sc_cdev), "spigen%d.%d", 217 device_get_unit(device_get_parent(dev)), cs); 218 if (res) { 219 return res; 220 } 221 222 #ifdef SPIGEN_LEGACY_CDEVNAME 223 res = make_dev_alias_p(0, &sc->sc_adev, sc->sc_cdev, "spigen%d", unit); 224 if (res) { 225 if (sc->sc_cdev) { 226 destroy_dev(sc->sc_cdev); 227 sc->sc_cdev = NULL; 228 } 229 return res; 230 } 231 #endif 232 233 spigen_sysctl_init(sc); 234 235 return (0); 236 } 237 238 static int 239 spigen_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 240 { 241 int error; 242 device_t dev; 243 struct spigen_softc *sc; 244 245 error = 0; 246 dev = cdev->si_drv1; 247 sc = device_get_softc(dev); 248 249 mtx_lock(&sc->sc_mtx); 250 if (sc->sc_flags & SPIGEN_OPEN) 251 error = EBUSY; 252 else 253 sc->sc_flags |= SPIGEN_OPEN; 254 mtx_unlock(&sc->sc_mtx); 255 256 return (error); 257 } 258 259 static int 260 spigen_transfer(struct cdev *cdev, struct spigen_transfer *st) 261 { 262 struct spi_command transfer = SPI_COMMAND_INITIALIZER; 263 device_t dev = cdev->si_drv1; 264 struct spigen_softc *sc = device_get_softc(dev); 265 int error = 0; 266 267 mtx_lock(&sc->sc_mtx); 268 if (st->st_command.iov_len == 0) 269 error = EINVAL; 270 else if (st->st_command.iov_len > sc->sc_command_length_max || 271 st->st_data.iov_len > sc->sc_data_length_max) 272 error = ENOMEM; 273 mtx_unlock(&sc->sc_mtx); 274 if (error) 275 return (error); 276 277 #if 0 278 device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base, 279 st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len); 280 #endif 281 transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len, 282 M_DEVBUF, M_WAITOK); 283 if (st->st_data.iov_len > 0) { 284 transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len, 285 M_DEVBUF, M_WAITOK); 286 } 287 else 288 transfer.tx_data = transfer.rx_data = NULL; 289 290 error = copyin(st->st_command.iov_base, transfer.tx_cmd, 291 transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len); 292 if ((error == 0) && (st->st_data.iov_len > 0)) 293 error = copyin(st->st_data.iov_base, transfer.tx_data, 294 transfer.tx_data_sz = transfer.rx_data_sz = 295 st->st_data.iov_len); 296 if (error == 0) 297 error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 298 if (error == 0) { 299 error = copyout(transfer.rx_cmd, st->st_command.iov_base, 300 transfer.rx_cmd_sz); 301 if ((error == 0) && (st->st_data.iov_len > 0)) 302 error = copyout(transfer.rx_data, st->st_data.iov_base, 303 transfer.rx_data_sz); 304 } 305 306 free(transfer.tx_cmd, M_DEVBUF); 307 free(transfer.tx_data, M_DEVBUF); 308 return (error); 309 } 310 311 static int 312 spigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm) 313 { 314 struct spi_command transfer = SPI_COMMAND_INITIALIZER; 315 device_t dev = cdev->si_drv1; 316 struct spigen_softc *sc = device_get_softc(dev); 317 int error = 0; 318 319 mtx_lock(&sc->sc_mtx); 320 if (sc->sc_flags & SPIGEN_MMAP_BUSY) 321 error = EBUSY; 322 else if (stm->stm_command_length > sc->sc_command_length_max || 323 stm->stm_data_length > sc->sc_data_length_max) 324 error = E2BIG; 325 else if (sc->sc_mmap_buffer == NULL) 326 error = EINVAL; 327 else if (sc->sc_mmap_buffer_size < 328 stm->stm_command_length + stm->stm_data_length) 329 error = ENOMEM; 330 if (error == 0) 331 sc->sc_flags |= SPIGEN_MMAP_BUSY; 332 mtx_unlock(&sc->sc_mtx); 333 if (error) 334 return (error); 335 336 transfer.tx_cmd = transfer.rx_cmd = (void *)sc->sc_mmap_kvaddr; 337 transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length; 338 transfer.tx_data = transfer.rx_data = 339 (void *)(sc->sc_mmap_kvaddr + stm->stm_command_length); 340 transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length; 341 error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 342 343 mtx_lock(&sc->sc_mtx); 344 KASSERT((sc->sc_flags & SPIGEN_MMAP_BUSY), ("mmap no longer marked busy")); 345 sc->sc_flags &= ~(SPIGEN_MMAP_BUSY); 346 mtx_unlock(&sc->sc_mtx); 347 return (error); 348 } 349 350 static int 351 spigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 352 struct thread *td) 353 { 354 device_t dev = cdev->si_drv1; 355 int error; 356 357 switch (cmd) { 358 case SPIGENIOC_TRANSFER: 359 error = spigen_transfer(cdev, (struct spigen_transfer *)data); 360 break; 361 case SPIGENIOC_TRANSFER_MMAPPED: 362 error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data); 363 break; 364 case SPIGENIOC_GET_CLOCK_SPEED: 365 error = spibus_get_clock(dev, (uint32_t *)data); 366 break; 367 case SPIGENIOC_SET_CLOCK_SPEED: 368 error = spibus_set_clock(dev, *(uint32_t *)data); 369 break; 370 case SPIGENIOC_GET_SPI_MODE: 371 error = spibus_get_mode(dev, (uint32_t *)data); 372 break; 373 case SPIGENIOC_SET_SPI_MODE: 374 error = spibus_set_mode(dev, *(uint32_t *)data); 375 break; 376 default: 377 error = ENOTTY; 378 break; 379 } 380 return (error); 381 } 382 383 static int 384 spigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 385 vm_size_t size, struct vm_object **object, int nprot) 386 { 387 device_t dev = cdev->si_drv1; 388 struct spigen_softc *sc = device_get_softc(dev); 389 vm_page_t *m; 390 size_t n, pages; 391 392 if (size == 0 || 393 (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE)) 394 != (PROT_READ | PROT_WRITE)) 395 return (EINVAL); 396 size = roundup2(size, PAGE_SIZE); 397 pages = size / PAGE_SIZE; 398 399 mtx_lock(&sc->sc_mtx); 400 if (sc->sc_mmap_buffer != NULL) { 401 mtx_unlock(&sc->sc_mtx); 402 return (EBUSY); 403 } else if (size > sc->sc_command_length_max + sc->sc_data_length_max) { 404 mtx_unlock(&sc->sc_mtx); 405 return (E2BIG); 406 } 407 sc->sc_mmap_buffer_size = size; 408 *offset = 0; 409 sc->sc_mmap_buffer = *object = vm_pager_allocate(OBJT_PHYS, 0, size, 410 nprot, *offset, curthread->td_ucred); 411 m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK); 412 VM_OBJECT_WLOCK(*object); 413 vm_object_reference_locked(*object); // kernel and userland both 414 for (n = 0; n < pages; n++) { 415 m[n] = vm_page_grab(*object, n, 416 VM_ALLOC_NOBUSY | VM_ALLOC_ZERO | VM_ALLOC_WIRED); 417 m[n]->valid = VM_PAGE_BITS_ALL; 418 } 419 VM_OBJECT_WUNLOCK(*object); 420 sc->sc_mmap_kvaddr = kva_alloc(size); 421 pmap_qenter(sc->sc_mmap_kvaddr, m, pages); 422 free(m, M_TEMP); 423 mtx_unlock(&sc->sc_mtx); 424 425 if (*object == NULL) 426 return (EINVAL); 427 return (0); 428 } 429 430 static int 431 spigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 432 { 433 device_t dev = cdev->si_drv1; 434 struct spigen_softc *sc = device_get_softc(dev); 435 436 mtx_lock(&sc->sc_mtx); 437 if (sc->sc_mmap_buffer != NULL) { 438 pmap_qremove(sc->sc_mmap_kvaddr, 439 sc->sc_mmap_buffer_size / PAGE_SIZE); 440 kva_free(sc->sc_mmap_kvaddr, sc->sc_mmap_buffer_size); 441 sc->sc_mmap_kvaddr = 0; 442 vm_object_deallocate(sc->sc_mmap_buffer); 443 sc->sc_mmap_buffer = NULL; 444 sc->sc_mmap_buffer_size = 0; 445 } 446 sc->sc_flags &= ~(SPIGEN_OPEN); 447 mtx_unlock(&sc->sc_mtx); 448 return (0); 449 } 450 451 static int 452 spigen_detach(device_t dev) 453 { 454 struct spigen_softc *sc; 455 456 sc = device_get_softc(dev); 457 458 mtx_lock(&sc->sc_mtx); 459 if (sc->sc_flags & SPIGEN_OPEN) { 460 mtx_unlock(&sc->sc_mtx); 461 return (EBUSY); 462 } 463 mtx_unlock(&sc->sc_mtx); 464 465 mtx_destroy(&sc->sc_mtx); 466 467 #ifdef SPIGEN_LEGACY_CDEVNAME 468 if (sc->sc_adev) 469 destroy_dev(sc->sc_adev); 470 #endif 471 472 if (sc->sc_cdev) 473 destroy_dev(sc->sc_cdev); 474 475 return (0); 476 } 477 478 static devclass_t spigen_devclass; 479 480 static device_method_t spigen_methods[] = { 481 /* Device interface */ 482 DEVMETHOD(device_probe, spigen_probe), 483 DEVMETHOD(device_attach, spigen_attach), 484 DEVMETHOD(device_detach, spigen_detach), 485 486 { 0, 0 } 487 }; 488 489 static driver_t spigen_driver = { 490 "spigen", 491 spigen_methods, 492 sizeof(struct spigen_softc), 493 }; 494 495 DRIVER_MODULE(spigen, spibus, spigen_driver, spigen_devclass, 0, 0); 496 MODULE_DEPEND(spigen, spibus, 1, 1, 1); 497