spigen.c revision 329278
1300713Sadrian/*- 2300713Sadrian * Copyright (c) 2015 Brian Fundakowski Feldman. All rights reserved. 3300713Sadrian * 4300713Sadrian * Redistribution and use in source and binary forms, with or without 5300713Sadrian * modification, are permitted provided that the following conditions 6300713Sadrian * are met: 7300713Sadrian * 1. Redistributions of source code must retain the above copyright 8300713Sadrian * notice, this list of conditions and the following disclaimer. 9300713Sadrian * 2. Redistributions in binary form must reproduce the above copyright 10300713Sadrian * notice, this list of conditions and the following disclaimer in the 11300713Sadrian * documentation and/or other materials provided with the distribution. 12300713Sadrian * 13300713Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14300713Sadrian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15300713Sadrian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16300713Sadrian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17300713Sadrian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18300713Sadrian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19300713Sadrian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20300713Sadrian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21300713Sadrian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22300713Sadrian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23300713Sadrian */ 24300713Sadrian 25300713Sadrian#include <sys/cdefs.h> 26300713Sadrian__FBSDID("$FreeBSD: stable/11/sys/dev/spibus/spigen.c 329278 2018-02-14 21:14:28Z gonzo $"); 27300713Sadrian 28329272Sgonzo#include "opt_platform.h" 29329272Sgonzo 30300713Sadrian#include <sys/param.h> 31300713Sadrian#include <sys/systm.h> 32300713Sadrian#include <sys/bus.h> 33300713Sadrian#include <sys/conf.h> 34300713Sadrian#include <sys/kernel.h> 35300713Sadrian#include <sys/lock.h> 36300713Sadrian#include <sys/malloc.h> 37300713Sadrian#include <sys/mman.h> 38300713Sadrian#include <sys/mutex.h> 39300713Sadrian#include <sys/module.h> 40300713Sadrian#include <sys/proc.h> 41300713Sadrian#include <sys/rwlock.h> 42300713Sadrian#include <sys/spigenio.h> 43300713Sadrian#include <sys/sysctl.h> 44300713Sadrian#include <sys/types.h> 45300713Sadrian 46300713Sadrian#include <vm/vm.h> 47300713Sadrian#include <vm/vm_extern.h> 48300713Sadrian#include <vm/vm_object.h> 49300713Sadrian#include <vm/vm_page.h> 50300713Sadrian#include <vm/vm_pager.h> 51300713Sadrian 52300713Sadrian#include <dev/spibus/spi.h> 53300713Sadrian 54300713Sadrian#include "spibus_if.h" 55300713Sadrian 56329278Sgonzo#define SPIGEN_OPEN (1 << 0) 57329278Sgonzo#define SPIGEN_MMAP_BUSY (1 << 1) 58329278Sgonzo 59300713Sadrianstruct spigen_softc { 60300713Sadrian device_t sc_dev; 61300713Sadrian struct cdev *sc_cdev; 62300713Sadrian struct mtx sc_mtx; 63300713Sadrian uint32_t sc_clock_speed; 64300713Sadrian uint32_t sc_command_length_max; /* cannot change while mmapped */ 65300713Sadrian uint32_t sc_data_length_max; /* cannot change while mmapped */ 66300713Sadrian vm_object_t sc_mmap_buffer; /* command, then data */ 67300713Sadrian vm_offset_t sc_mmap_kvaddr; 68300713Sadrian size_t sc_mmap_buffer_size; 69300713Sadrian int sc_debug; 70329278Sgonzo int sc_flags; 71300713Sadrian}; 72300713Sadrian 73329272Sgonzo#ifdef FDT 74329272Sgonzostatic void 75329272Sgonzospigen_identify(driver_t *driver, device_t parent) 76329272Sgonzo{ 77329272Sgonzo if (device_find_child(parent, "spigen", -1) != NULL) 78329272Sgonzo return; 79329272Sgonzo if (BUS_ADD_CHILD(parent, 0, "spigen", -1) == NULL) 80329272Sgonzo device_printf(parent, "add child failed\n"); 81329272Sgonzo} 82329272Sgonzo#endif 83329272Sgonzo 84300713Sadrianstatic int 85300713Sadrianspigen_probe(device_t dev) 86300713Sadrian{ 87311342Sgonzo 88300713Sadrian device_set_desc(dev, "SPI Generic IO"); 89311342Sgonzo 90311342Sgonzo return (BUS_PROBE_NOWILDCARD); 91300713Sadrian} 92300713Sadrian 93300713Sadrianstatic int spigen_open(struct cdev *, int, int, struct thread *); 94300713Sadrianstatic int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 95300713Sadrianstatic int spigen_close(struct cdev *, int, int, struct thread *); 96300713Sadrianstatic d_mmap_single_t spigen_mmap_single; 97300713Sadrian 98300713Sadrianstatic struct cdevsw spigen_cdevsw = { 99300713Sadrian .d_version = D_VERSION, 100300713Sadrian .d_name = "spigen", 101300713Sadrian .d_open = spigen_open, 102300713Sadrian .d_ioctl = spigen_ioctl, 103300713Sadrian .d_mmap_single = spigen_mmap_single, 104300713Sadrian .d_close = spigen_close 105300713Sadrian}; 106300713Sadrian 107300713Sadrianstatic int 108300713Sadrianspigen_command_length_max_proc(SYSCTL_HANDLER_ARGS) 109300713Sadrian{ 110300713Sadrian struct spigen_softc *sc = (struct spigen_softc *)arg1; 111300713Sadrian uint32_t command_length_max; 112300713Sadrian int error; 113300713Sadrian 114300713Sadrian mtx_lock(&sc->sc_mtx); 115300713Sadrian command_length_max = sc->sc_command_length_max; 116300713Sadrian mtx_unlock(&sc->sc_mtx); 117300713Sadrian error = sysctl_handle_int(oidp, &command_length_max, 118300713Sadrian sizeof(command_length_max), req); 119300713Sadrian if (error == 0 && req->newptr != NULL) { 120300713Sadrian mtx_lock(&sc->sc_mtx); 121300713Sadrian if (sc->sc_mmap_buffer != NULL) 122300713Sadrian error = EBUSY; 123300713Sadrian else 124300713Sadrian sc->sc_command_length_max = command_length_max; 125300713Sadrian mtx_unlock(&sc->sc_mtx); 126300713Sadrian } 127300713Sadrian return (error); 128300713Sadrian} 129300713Sadrian 130300713Sadrianstatic int 131300713Sadrianspigen_data_length_max_proc(SYSCTL_HANDLER_ARGS) 132300713Sadrian{ 133300713Sadrian struct spigen_softc *sc = (struct spigen_softc *)arg1; 134300713Sadrian uint32_t data_length_max; 135300713Sadrian int error; 136300713Sadrian 137300713Sadrian mtx_lock(&sc->sc_mtx); 138300713Sadrian data_length_max = sc->sc_data_length_max; 139300713Sadrian mtx_unlock(&sc->sc_mtx); 140300713Sadrian error = sysctl_handle_int(oidp, &data_length_max, 141300713Sadrian sizeof(data_length_max), req); 142300713Sadrian if (error == 0 && req->newptr != NULL) { 143300713Sadrian mtx_lock(&sc->sc_mtx); 144300713Sadrian if (sc->sc_mmap_buffer != NULL) 145300713Sadrian error = EBUSY; 146300713Sadrian else 147300713Sadrian sc->sc_data_length_max = data_length_max; 148300713Sadrian mtx_unlock(&sc->sc_mtx); 149300713Sadrian } 150300713Sadrian return (error); 151300713Sadrian} 152300713Sadrian 153300713Sadrianstatic void 154300713Sadrianspigen_sysctl_init(struct spigen_softc *sc) 155300713Sadrian{ 156300713Sadrian struct sysctl_ctx_list *ctx; 157300713Sadrian struct sysctl_oid *tree_node; 158300713Sadrian struct sysctl_oid_list *tree; 159300713Sadrian 160300713Sadrian /* 161300713Sadrian * Add system sysctl tree/handlers. 162300713Sadrian */ 163300713Sadrian ctx = device_get_sysctl_ctx(sc->sc_dev); 164300713Sadrian tree_node = device_get_sysctl_tree(sc->sc_dev); 165300713Sadrian tree = SYSCTL_CHILDREN(tree_node); 166300713Sadrian SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "command_length_max", 167300713Sadrian CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 168300713Sadrian spigen_command_length_max_proc, "IU", "SPI command header portion (octets)"); 169300713Sadrian SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "data_length_max", 170300713Sadrian CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 171300713Sadrian spigen_data_length_max_proc, "IU", "SPI data trailer portion (octets)"); 172300713Sadrian SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "data", CTLFLAG_RW, 173300713Sadrian &sc->sc_debug, 0, "debug flags"); 174300713Sadrian 175300713Sadrian} 176300713Sadrian 177300713Sadrianstatic int 178300713Sadrianspigen_attach(device_t dev) 179300713Sadrian{ 180300713Sadrian struct spigen_softc *sc; 181300713Sadrian const int unit = device_get_unit(dev); 182300713Sadrian 183300713Sadrian sc = device_get_softc(dev); 184300713Sadrian sc->sc_dev = dev; 185300713Sadrian sc->sc_cdev = make_dev(&spigen_cdevsw, unit, 186300713Sadrian UID_ROOT, GID_OPERATOR, 0660, "spigen%d", unit); 187300713Sadrian sc->sc_cdev->si_drv1 = dev; 188300713Sadrian sc->sc_command_length_max = PAGE_SIZE; 189300713Sadrian sc->sc_data_length_max = PAGE_SIZE; 190300713Sadrian mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 191300713Sadrian spigen_sysctl_init(sc); 192300713Sadrian 193300713Sadrian return (0); 194300713Sadrian} 195300713Sadrian 196300713Sadrianstatic int 197329278Sgonzospigen_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 198300713Sadrian{ 199329278Sgonzo int error; 200329278Sgonzo device_t dev; 201329278Sgonzo struct spigen_softc *sc; 202300713Sadrian 203329278Sgonzo error = 0; 204329278Sgonzo dev = cdev->si_drv1; 205329278Sgonzo sc = device_get_softc(dev); 206329278Sgonzo 207329278Sgonzo mtx_lock(&sc->sc_mtx); 208329278Sgonzo if (sc->sc_flags & SPIGEN_OPEN) 209329278Sgonzo error = EBUSY; 210329278Sgonzo else 211329278Sgonzo sc->sc_flags |= SPIGEN_OPEN; 212329278Sgonzo mtx_unlock(&sc->sc_mtx); 213329278Sgonzo 214329278Sgonzo return (error); 215300713Sadrian} 216300713Sadrian 217300713Sadrianstatic int 218300713Sadrianspigen_transfer(struct cdev *cdev, struct spigen_transfer *st) 219300713Sadrian{ 220300713Sadrian struct spi_command transfer = SPI_COMMAND_INITIALIZER; 221300713Sadrian device_t dev = cdev->si_drv1; 222300713Sadrian struct spigen_softc *sc = device_get_softc(dev); 223300713Sadrian int error = 0; 224300713Sadrian 225300713Sadrian mtx_lock(&sc->sc_mtx); 226311342Sgonzo if (st->st_command.iov_len == 0) 227300713Sadrian error = EINVAL; 228300713Sadrian else if (st->st_command.iov_len > sc->sc_command_length_max || 229300713Sadrian st->st_data.iov_len > sc->sc_data_length_max) 230300713Sadrian error = ENOMEM; 231300713Sadrian mtx_unlock(&sc->sc_mtx); 232300713Sadrian if (error) 233300713Sadrian return (error); 234300713Sadrian 235300713Sadrian#if 0 236300713Sadrian device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base, 237300713Sadrian st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len); 238300713Sadrian#endif 239300713Sadrian transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len, 240300713Sadrian M_DEVBUF, M_WAITOK); 241300713Sadrian if (transfer.tx_cmd == NULL) 242300713Sadrian return (ENOMEM); 243311342Sgonzo if (st->st_data.iov_len > 0) { 244311342Sgonzo transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len, 245311342Sgonzo M_DEVBUF, M_WAITOK); 246311342Sgonzo if (transfer.tx_data == NULL) { 247311342Sgonzo free(transfer.tx_cmd, M_DEVBUF); 248311342Sgonzo return (ENOMEM); 249311342Sgonzo } 250300713Sadrian } 251311342Sgonzo else 252311342Sgonzo transfer.tx_data = transfer.rx_data = NULL; 253300713Sadrian 254300713Sadrian error = copyin(st->st_command.iov_base, transfer.tx_cmd, 255300713Sadrian transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len); 256311342Sgonzo if ((error == 0) && (st->st_data.iov_len > 0)) 257300713Sadrian error = copyin(st->st_data.iov_base, transfer.tx_data, 258300713Sadrian transfer.tx_data_sz = transfer.rx_data_sz = 259300713Sadrian st->st_data.iov_len); 260300713Sadrian if (error == 0) 261300713Sadrian error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 262300713Sadrian if (error == 0) { 263300713Sadrian error = copyout(transfer.rx_cmd, st->st_command.iov_base, 264300713Sadrian transfer.rx_cmd_sz); 265311342Sgonzo if ((error == 0) && (st->st_data.iov_len > 0)) 266300713Sadrian error = copyout(transfer.rx_data, st->st_data.iov_base, 267300713Sadrian transfer.rx_data_sz); 268300713Sadrian } 269300713Sadrian 270300713Sadrian free(transfer.tx_cmd, M_DEVBUF); 271300713Sadrian free(transfer.tx_data, M_DEVBUF); 272300713Sadrian return (error); 273300713Sadrian} 274300713Sadrian 275300713Sadrianstatic int 276300713Sadrianspigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm) 277300713Sadrian{ 278300713Sadrian struct spi_command transfer = SPI_COMMAND_INITIALIZER; 279300713Sadrian device_t dev = cdev->si_drv1; 280300713Sadrian struct spigen_softc *sc = device_get_softc(dev); 281300713Sadrian int error = 0; 282300713Sadrian 283300713Sadrian mtx_lock(&sc->sc_mtx); 284329278Sgonzo if (sc->sc_flags & SPIGEN_MMAP_BUSY) 285300713Sadrian error = EBUSY; 286300713Sadrian else if (stm->stm_command_length > sc->sc_command_length_max || 287300713Sadrian stm->stm_data_length > sc->sc_data_length_max) 288300713Sadrian error = E2BIG; 289300713Sadrian else if (sc->sc_mmap_buffer == NULL) 290300713Sadrian error = EINVAL; 291300713Sadrian else if (sc->sc_mmap_buffer_size < 292300713Sadrian stm->stm_command_length + stm->stm_data_length) 293300713Sadrian error = ENOMEM; 294300713Sadrian if (error == 0) 295329278Sgonzo sc->sc_flags |= SPIGEN_MMAP_BUSY; 296300713Sadrian mtx_unlock(&sc->sc_mtx); 297300713Sadrian if (error) 298300713Sadrian return (error); 299300713Sadrian 300300713Sadrian transfer.tx_cmd = transfer.rx_cmd = (void *)sc->sc_mmap_kvaddr; 301300713Sadrian transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length; 302300713Sadrian transfer.tx_data = transfer.rx_data = 303300713Sadrian (void *)(sc->sc_mmap_kvaddr + stm->stm_command_length); 304300713Sadrian transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length; 305300713Sadrian error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 306300713Sadrian 307300713Sadrian mtx_lock(&sc->sc_mtx); 308329278Sgonzo KASSERT((sc->sc_flags & SPIGEN_MMAP_BUSY), ("mmap no longer marked busy")); 309329278Sgonzo sc->sc_flags &= ~(SPIGEN_MMAP_BUSY); 310300713Sadrian mtx_unlock(&sc->sc_mtx); 311300713Sadrian return (error); 312300713Sadrian} 313300713Sadrian 314300713Sadrianstatic int 315300713Sadrianspigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 316300713Sadrian struct thread *td) 317300713Sadrian{ 318300713Sadrian device_t dev = cdev->si_drv1; 319300713Sadrian struct spigen_softc *sc = device_get_softc(dev); 320300713Sadrian int error; 321300713Sadrian 322300713Sadrian switch (cmd) { 323300713Sadrian case SPIGENIOC_TRANSFER: 324300713Sadrian error = spigen_transfer(cdev, (struct spigen_transfer *)data); 325300713Sadrian break; 326300713Sadrian case SPIGENIOC_TRANSFER_MMAPPED: 327300713Sadrian error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data); 328300713Sadrian break; 329300713Sadrian case SPIGENIOC_GET_CLOCK_SPEED: 330300713Sadrian mtx_lock(&sc->sc_mtx); 331300713Sadrian *(uint32_t *)data = sc->sc_clock_speed; 332300713Sadrian /* XXX TODO: implement spibus ivar call */ 333300713Sadrian mtx_unlock(&sc->sc_mtx); 334300713Sadrian error = 0; 335300713Sadrian break; 336300713Sadrian case SPIGENIOC_SET_CLOCK_SPEED: 337300713Sadrian mtx_lock(&sc->sc_mtx); 338300713Sadrian sc->sc_clock_speed = *(uint32_t *)data; 339300713Sadrian mtx_unlock(&sc->sc_mtx); 340300713Sadrian error = 0; 341300713Sadrian break; 342300713Sadrian default: 343300713Sadrian error = EOPNOTSUPP; 344300713Sadrian } 345300713Sadrian return (error); 346300713Sadrian} 347300713Sadrian 348300713Sadrianstatic int 349300713Sadrianspigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 350300713Sadrian vm_size_t size, struct vm_object **object, int nprot) 351300713Sadrian{ 352300713Sadrian device_t dev = cdev->si_drv1; 353300713Sadrian struct spigen_softc *sc = device_get_softc(dev); 354300713Sadrian vm_page_t *m; 355300713Sadrian size_t n, pages; 356300713Sadrian 357300713Sadrian if (size == 0 || 358300713Sadrian (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE)) 359300713Sadrian != (PROT_READ | PROT_WRITE)) 360300713Sadrian return (EINVAL); 361300713Sadrian size = roundup2(size, PAGE_SIZE); 362300713Sadrian pages = size / PAGE_SIZE; 363300713Sadrian 364300713Sadrian mtx_lock(&sc->sc_mtx); 365300713Sadrian if (sc->sc_mmap_buffer != NULL) { 366300713Sadrian mtx_unlock(&sc->sc_mtx); 367300713Sadrian return (EBUSY); 368300713Sadrian } else if (size > sc->sc_command_length_max + sc->sc_data_length_max) { 369300713Sadrian mtx_unlock(&sc->sc_mtx); 370300713Sadrian return (E2BIG); 371300713Sadrian } 372300713Sadrian sc->sc_mmap_buffer_size = size; 373300713Sadrian *offset = 0; 374300713Sadrian sc->sc_mmap_buffer = *object = vm_pager_allocate(OBJT_PHYS, 0, size, 375300713Sadrian nprot, *offset, curthread->td_ucred); 376300713Sadrian m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK); 377300713Sadrian VM_OBJECT_WLOCK(*object); 378300713Sadrian vm_object_reference_locked(*object); // kernel and userland both 379300713Sadrian for (n = 0; n < pages; n++) { 380300713Sadrian m[n] = vm_page_grab(*object, n, 381300713Sadrian VM_ALLOC_NOBUSY | VM_ALLOC_ZERO | VM_ALLOC_WIRED); 382300713Sadrian m[n]->valid = VM_PAGE_BITS_ALL; 383300713Sadrian } 384300713Sadrian VM_OBJECT_WUNLOCK(*object); 385300713Sadrian sc->sc_mmap_kvaddr = kva_alloc(size); 386300713Sadrian pmap_qenter(sc->sc_mmap_kvaddr, m, pages); 387300713Sadrian free(m, M_TEMP); 388300713Sadrian mtx_unlock(&sc->sc_mtx); 389300713Sadrian 390300713Sadrian if (*object == NULL) 391300713Sadrian return (EINVAL); 392300713Sadrian return (0); 393300713Sadrian} 394300713Sadrian 395300713Sadrianstatic int 396300713Sadrianspigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 397300713Sadrian{ 398300713Sadrian device_t dev = cdev->si_drv1; 399300713Sadrian struct spigen_softc *sc = device_get_softc(dev); 400300713Sadrian 401300713Sadrian mtx_lock(&sc->sc_mtx); 402300713Sadrian if (sc->sc_mmap_buffer != NULL) { 403300713Sadrian pmap_qremove(sc->sc_mmap_kvaddr, 404300713Sadrian sc->sc_mmap_buffer_size / PAGE_SIZE); 405300713Sadrian kva_free(sc->sc_mmap_kvaddr, sc->sc_mmap_buffer_size); 406300713Sadrian sc->sc_mmap_kvaddr = 0; 407300713Sadrian vm_object_deallocate(sc->sc_mmap_buffer); 408300713Sadrian sc->sc_mmap_buffer = NULL; 409300713Sadrian sc->sc_mmap_buffer_size = 0; 410300713Sadrian } 411329278Sgonzo sc->sc_flags &= ~(SPIGEN_OPEN); 412300713Sadrian mtx_unlock(&sc->sc_mtx); 413300713Sadrian return (0); 414300713Sadrian} 415300713Sadrian 416300713Sadrianstatic int 417300713Sadrianspigen_detach(device_t dev) 418300713Sadrian{ 419329278Sgonzo struct spigen_softc *sc; 420300713Sadrian 421329278Sgonzo sc = device_get_softc(dev); 422329278Sgonzo 423329278Sgonzo mtx_lock(&sc->sc_mtx); 424329278Sgonzo if (sc->sc_flags & SPIGEN_OPEN) { 425329278Sgonzo mtx_unlock(&sc->sc_mtx); 426329278Sgonzo return (EBUSY); 427329278Sgonzo } 428329278Sgonzo mtx_unlock(&sc->sc_mtx); 429329278Sgonzo 430329278Sgonzo mtx_destroy(&sc->sc_mtx); 431329278Sgonzo 432329278Sgonzo if (sc->sc_cdev) 433329278Sgonzo destroy_dev(sc->sc_cdev); 434329278Sgonzo 435329278Sgonzo return (0); 436300713Sadrian} 437300713Sadrian 438300713Sadrianstatic devclass_t spigen_devclass; 439300713Sadrian 440300713Sadrianstatic device_method_t spigen_methods[] = { 441300713Sadrian /* Device interface */ 442329272Sgonzo#ifdef FDT 443329272Sgonzo DEVMETHOD(device_identify, spigen_identify), 444329272Sgonzo#endif 445300713Sadrian DEVMETHOD(device_probe, spigen_probe), 446300713Sadrian DEVMETHOD(device_attach, spigen_attach), 447300713Sadrian DEVMETHOD(device_detach, spigen_detach), 448300713Sadrian 449300713Sadrian { 0, 0 } 450300713Sadrian}; 451300713Sadrian 452300713Sadrianstatic driver_t spigen_driver = { 453300713Sadrian "spigen", 454300713Sadrian spigen_methods, 455300713Sadrian sizeof(struct spigen_softc), 456300713Sadrian}; 457300713Sadrian 458300713SadrianDRIVER_MODULE(spigen, spibus, spigen_driver, spigen_devclass, 0, 0); 459