1d67fe28bSAdrian Chadd /*- 2d67fe28bSAdrian Chadd * Copyright (c) 2015 Brian Fundakowski Feldman. All rights reserved. 3d67fe28bSAdrian Chadd * 4d67fe28bSAdrian Chadd * Redistribution and use in source and binary forms, with or without 5d67fe28bSAdrian Chadd * modification, are permitted provided that the following conditions 6d67fe28bSAdrian Chadd * are met: 7d67fe28bSAdrian Chadd * 1. Redistributions of source code must retain the above copyright 8d67fe28bSAdrian Chadd * notice, this list of conditions and the following disclaimer. 9d67fe28bSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 10d67fe28bSAdrian Chadd * notice, this list of conditions and the following disclaimer in the 11d67fe28bSAdrian Chadd * documentation and/or other materials provided with the distribution. 12d67fe28bSAdrian Chadd * 13d67fe28bSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14d67fe28bSAdrian Chadd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15d67fe28bSAdrian Chadd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16d67fe28bSAdrian Chadd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17d67fe28bSAdrian Chadd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18d67fe28bSAdrian Chadd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19d67fe28bSAdrian Chadd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20d67fe28bSAdrian Chadd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21d67fe28bSAdrian Chadd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22d67fe28bSAdrian Chadd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23d67fe28bSAdrian Chadd */ 24d67fe28bSAdrian Chadd 25d67fe28bSAdrian Chadd #include <sys/cdefs.h> 26d67fe28bSAdrian Chadd __FBSDID("$FreeBSD$"); 27d67fe28bSAdrian Chadd 28dac458e0SEmmanuel Vadot #include "opt_platform.h" 291fcf4de0SIan Lepore #include "opt_spi.h" 30dac458e0SEmmanuel Vadot 31d67fe28bSAdrian Chadd #include <sys/param.h> 32d67fe28bSAdrian Chadd #include <sys/systm.h> 33d67fe28bSAdrian Chadd #include <sys/bus.h> 34d67fe28bSAdrian Chadd #include <sys/conf.h> 35d67fe28bSAdrian Chadd #include <sys/kernel.h> 36d67fe28bSAdrian Chadd #include <sys/lock.h> 37d67fe28bSAdrian Chadd #include <sys/malloc.h> 38d67fe28bSAdrian Chadd #include <sys/mman.h> 39d67fe28bSAdrian Chadd #include <sys/mutex.h> 40d67fe28bSAdrian Chadd #include <sys/module.h> 41d67fe28bSAdrian Chadd #include <sys/proc.h> 42d67fe28bSAdrian Chadd #include <sys/rwlock.h> 43d67fe28bSAdrian Chadd #include <sys/spigenio.h> 44d67fe28bSAdrian Chadd #include <sys/types.h> 45d67fe28bSAdrian Chadd 46d67fe28bSAdrian Chadd #include <vm/vm.h> 47d67fe28bSAdrian Chadd #include <vm/vm_extern.h> 48d67fe28bSAdrian Chadd #include <vm/vm_object.h> 49d67fe28bSAdrian Chadd #include <vm/vm_page.h> 50d67fe28bSAdrian Chadd #include <vm/vm_pager.h> 51d67fe28bSAdrian Chadd 52d67fe28bSAdrian Chadd #include <dev/spibus/spi.h> 53a0e911e0SIan Lepore #include <dev/spibus/spibusvar.h> 54d67fe28bSAdrian Chadd 553b46d868SIan Lepore #ifdef FDT 563b46d868SIan Lepore #include <dev/ofw/ofw_bus_subr.h> 577a4f1915SIan Lepore 587a4f1915SIan Lepore static struct ofw_compat_data compat_data[] = { 597a4f1915SIan Lepore {"freebsd,spigen", true}, 607a4f1915SIan Lepore {NULL, false} 617a4f1915SIan Lepore }; 627a4f1915SIan Lepore 633b46d868SIan Lepore #endif 643b46d868SIan Lepore 65d67fe28bSAdrian Chadd #include "spibus_if.h" 66d67fe28bSAdrian Chadd 67d67fe28bSAdrian Chadd struct spigen_softc { 68d67fe28bSAdrian Chadd device_t sc_dev; 69d67fe28bSAdrian Chadd struct cdev *sc_cdev; 701fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME 711fcf4de0SIan Lepore struct cdev *sc_adev; /* alias device */ 721fcf4de0SIan Lepore #endif 73d67fe28bSAdrian Chadd struct mtx sc_mtx; 747a4f1915SIan Lepore }; 757a4f1915SIan Lepore 767a4f1915SIan Lepore struct spigen_mmap { 777a4f1915SIan Lepore vm_object_t bufobj; 787a4f1915SIan Lepore vm_offset_t kvaddr; 797a4f1915SIan Lepore size_t bufsize; 80d67fe28bSAdrian Chadd }; 81d67fe28bSAdrian Chadd 82d67fe28bSAdrian Chadd static int 83d67fe28bSAdrian Chadd spigen_probe(device_t dev) 84d67fe28bSAdrian Chadd { 853b46d868SIan Lepore int rv; 8632d74127SOleksandr Tymoshenko 87bc7b44aeSIan Lepore /* 88bc7b44aeSIan Lepore * By default we only bid to attach if specifically added by our parent 89bc7b44aeSIan Lepore * (usually via hint.spigen.#.at=busname). On FDT systems we bid as the 90bc7b44aeSIan Lepore * default driver based on being configured in the FDT data. 91bc7b44aeSIan Lepore */ 923b46d868SIan Lepore rv = BUS_PROBE_NOWILDCARD; 93bc7b44aeSIan Lepore 94bc7b44aeSIan Lepore #ifdef FDT 95bc7b44aeSIan Lepore if (ofw_bus_status_okay(dev) && 967a4f1915SIan Lepore ofw_bus_search_compatible(dev, compat_data)->ocd_data) 97bc7b44aeSIan Lepore rv = BUS_PROBE_DEFAULT; 983b46d868SIan Lepore #endif 99bc7b44aeSIan Lepore 100d67fe28bSAdrian Chadd device_set_desc(dev, "SPI Generic IO"); 10132d74127SOleksandr Tymoshenko 1023b46d868SIan Lepore return (rv); 103d67fe28bSAdrian Chadd } 104d67fe28bSAdrian Chadd 105d67fe28bSAdrian Chadd static int spigen_open(struct cdev *, int, int, struct thread *); 106d67fe28bSAdrian Chadd static int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 107d67fe28bSAdrian Chadd static int spigen_close(struct cdev *, int, int, struct thread *); 108d67fe28bSAdrian Chadd static d_mmap_single_t spigen_mmap_single; 109d67fe28bSAdrian Chadd 110d67fe28bSAdrian Chadd static struct cdevsw spigen_cdevsw = { 111d67fe28bSAdrian Chadd .d_version = D_VERSION, 112d67fe28bSAdrian Chadd .d_name = "spigen", 113d67fe28bSAdrian Chadd .d_open = spigen_open, 114d67fe28bSAdrian Chadd .d_ioctl = spigen_ioctl, 115d67fe28bSAdrian Chadd .d_mmap_single = spigen_mmap_single, 116d67fe28bSAdrian Chadd .d_close = spigen_close 117d67fe28bSAdrian Chadd }; 118d67fe28bSAdrian Chadd 119d67fe28bSAdrian Chadd static int 120d67fe28bSAdrian Chadd spigen_attach(device_t dev) 121d67fe28bSAdrian Chadd { 122d67fe28bSAdrian Chadd struct spigen_softc *sc; 123d67fe28bSAdrian Chadd const int unit = device_get_unit(dev); 1241fcf4de0SIan Lepore int cs, res; 1251fcf4de0SIan Lepore struct make_dev_args mda; 1261fcf4de0SIan Lepore 1271fcf4de0SIan Lepore spibus_get_cs(dev, &cs); 1281fcf4de0SIan Lepore cs &= ~SPIBUS_CS_HIGH; /* trim 'cs high' bit */ 129d67fe28bSAdrian Chadd 130d67fe28bSAdrian Chadd sc = device_get_softc(dev); 131d67fe28bSAdrian Chadd sc->sc_dev = dev; 1321fcf4de0SIan Lepore 133d67fe28bSAdrian Chadd mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 1341fcf4de0SIan Lepore 1351fcf4de0SIan Lepore make_dev_args_init(&mda); 1361fcf4de0SIan Lepore mda.mda_flags = MAKEDEV_WAITOK; 1371fcf4de0SIan Lepore mda.mda_devsw = &spigen_cdevsw; 1381fcf4de0SIan Lepore mda.mda_cr = NULL; 1391fcf4de0SIan Lepore mda.mda_uid = UID_ROOT; 1401fcf4de0SIan Lepore mda.mda_gid = GID_OPERATOR; 1411fcf4de0SIan Lepore mda.mda_mode = 0660; 1421fcf4de0SIan Lepore mda.mda_unit = unit; 1431fcf4de0SIan Lepore mda.mda_si_drv1 = dev; 1441fcf4de0SIan Lepore 1451fcf4de0SIan Lepore res = make_dev_s(&mda, &(sc->sc_cdev), "spigen%d.%d", 1461fcf4de0SIan Lepore device_get_unit(device_get_parent(dev)), cs); 1471fcf4de0SIan Lepore if (res) { 1481fcf4de0SIan Lepore return res; 1491fcf4de0SIan Lepore } 1501fcf4de0SIan Lepore 1511fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME 1521fcf4de0SIan Lepore res = make_dev_alias_p(0, &sc->sc_adev, sc->sc_cdev, "spigen%d", unit); 1531fcf4de0SIan Lepore if (res) { 1541fcf4de0SIan Lepore if (sc->sc_cdev) { 1551fcf4de0SIan Lepore destroy_dev(sc->sc_cdev); 1561fcf4de0SIan Lepore sc->sc_cdev = NULL; 1571fcf4de0SIan Lepore } 1581fcf4de0SIan Lepore return res; 1591fcf4de0SIan Lepore } 1601fcf4de0SIan Lepore #endif 1611fcf4de0SIan Lepore 162d67fe28bSAdrian Chadd return (0); 163d67fe28bSAdrian Chadd } 164d67fe28bSAdrian Chadd 165d67fe28bSAdrian Chadd static int 16650868fa6SOleksandr Tymoshenko spigen_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 167d67fe28bSAdrian Chadd { 16850868fa6SOleksandr Tymoshenko device_t dev; 16950868fa6SOleksandr Tymoshenko struct spigen_softc *sc; 170d67fe28bSAdrian Chadd 17150868fa6SOleksandr Tymoshenko dev = cdev->si_drv1; 17250868fa6SOleksandr Tymoshenko sc = device_get_softc(dev); 17350868fa6SOleksandr Tymoshenko 17450868fa6SOleksandr Tymoshenko mtx_lock(&sc->sc_mtx); 1757a4f1915SIan Lepore device_busy(sc->sc_dev); 17650868fa6SOleksandr Tymoshenko mtx_unlock(&sc->sc_mtx); 17750868fa6SOleksandr Tymoshenko 1787a4f1915SIan Lepore return (0); 179d67fe28bSAdrian Chadd } 180d67fe28bSAdrian Chadd 181d67fe28bSAdrian Chadd static int 182d67fe28bSAdrian Chadd spigen_transfer(struct cdev *cdev, struct spigen_transfer *st) 183d67fe28bSAdrian Chadd { 184d67fe28bSAdrian Chadd struct spi_command transfer = SPI_COMMAND_INITIALIZER; 185d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1; 186d67fe28bSAdrian Chadd int error = 0; 187d67fe28bSAdrian Chadd 188d67fe28bSAdrian Chadd #if 0 189d67fe28bSAdrian Chadd device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base, 190d67fe28bSAdrian Chadd st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len); 191d67fe28bSAdrian Chadd #endif 1927a4f1915SIan Lepore 1937a4f1915SIan Lepore if (st->st_command.iov_len == 0) 1947a4f1915SIan Lepore return (EINVAL); 1957a4f1915SIan Lepore 196d67fe28bSAdrian Chadd transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len, 197d67fe28bSAdrian Chadd M_DEVBUF, M_WAITOK); 1983c43a826SOleksandr Tymoshenko if (st->st_data.iov_len > 0) { 199d67fe28bSAdrian Chadd transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len, 200d67fe28bSAdrian Chadd M_DEVBUF, M_WAITOK); 2013c43a826SOleksandr Tymoshenko } 2023c43a826SOleksandr Tymoshenko else 2033c43a826SOleksandr Tymoshenko transfer.tx_data = transfer.rx_data = NULL; 204d67fe28bSAdrian Chadd 205d67fe28bSAdrian Chadd error = copyin(st->st_command.iov_base, transfer.tx_cmd, 206d67fe28bSAdrian Chadd transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len); 2073c43a826SOleksandr Tymoshenko if ((error == 0) && (st->st_data.iov_len > 0)) 208d67fe28bSAdrian Chadd error = copyin(st->st_data.iov_base, transfer.tx_data, 209d67fe28bSAdrian Chadd transfer.tx_data_sz = transfer.rx_data_sz = 210d67fe28bSAdrian Chadd st->st_data.iov_len); 211d67fe28bSAdrian Chadd if (error == 0) 212d67fe28bSAdrian Chadd error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 213d67fe28bSAdrian Chadd if (error == 0) { 214d67fe28bSAdrian Chadd error = copyout(transfer.rx_cmd, st->st_command.iov_base, 215d67fe28bSAdrian Chadd transfer.rx_cmd_sz); 2163c43a826SOleksandr Tymoshenko if ((error == 0) && (st->st_data.iov_len > 0)) 217d67fe28bSAdrian Chadd error = copyout(transfer.rx_data, st->st_data.iov_base, 218d67fe28bSAdrian Chadd transfer.rx_data_sz); 219d67fe28bSAdrian Chadd } 220d67fe28bSAdrian Chadd 221d67fe28bSAdrian Chadd free(transfer.tx_cmd, M_DEVBUF); 222d67fe28bSAdrian Chadd free(transfer.tx_data, M_DEVBUF); 223d67fe28bSAdrian Chadd return (error); 224d67fe28bSAdrian Chadd } 225d67fe28bSAdrian Chadd 226d67fe28bSAdrian Chadd static int 227d67fe28bSAdrian Chadd spigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm) 228d67fe28bSAdrian Chadd { 229d67fe28bSAdrian Chadd struct spi_command transfer = SPI_COMMAND_INITIALIZER; 230d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1; 2317a4f1915SIan Lepore struct spigen_mmap *mmap; 2327a4f1915SIan Lepore int error; 233d67fe28bSAdrian Chadd 2347a4f1915SIan Lepore if ((error = devfs_get_cdevpriv((void **)&mmap)) != 0) 235d67fe28bSAdrian Chadd return (error); 236d67fe28bSAdrian Chadd 2377a4f1915SIan Lepore if (mmap->bufsize < stm->stm_command_length + stm->stm_data_length) 2387a4f1915SIan Lepore return (E2BIG); 2397a4f1915SIan Lepore 2407a4f1915SIan Lepore transfer.tx_cmd = transfer.rx_cmd = (void *)((uintptr_t)mmap->kvaddr); 241d67fe28bSAdrian Chadd transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length; 242d67fe28bSAdrian Chadd transfer.tx_data = transfer.rx_data = 2437a4f1915SIan Lepore (void *)((uintptr_t)mmap->kvaddr + stm->stm_command_length); 244d67fe28bSAdrian Chadd transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length; 245d67fe28bSAdrian Chadd error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer); 246d67fe28bSAdrian Chadd 247d67fe28bSAdrian Chadd return (error); 248d67fe28bSAdrian Chadd } 249d67fe28bSAdrian Chadd 250d67fe28bSAdrian Chadd static int 251d67fe28bSAdrian Chadd spigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 252d67fe28bSAdrian Chadd struct thread *td) 253d67fe28bSAdrian Chadd { 254d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1; 255d67fe28bSAdrian Chadd int error; 256d67fe28bSAdrian Chadd 257d67fe28bSAdrian Chadd switch (cmd) { 258d67fe28bSAdrian Chadd case SPIGENIOC_TRANSFER: 259d67fe28bSAdrian Chadd error = spigen_transfer(cdev, (struct spigen_transfer *)data); 260d67fe28bSAdrian Chadd break; 261d67fe28bSAdrian Chadd case SPIGENIOC_TRANSFER_MMAPPED: 262d67fe28bSAdrian Chadd error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data); 263d67fe28bSAdrian Chadd break; 264d67fe28bSAdrian Chadd case SPIGENIOC_GET_CLOCK_SPEED: 265197d784bSIan Lepore error = spibus_get_clock(dev, (uint32_t *)data); 266d67fe28bSAdrian Chadd break; 267d67fe28bSAdrian Chadd case SPIGENIOC_SET_CLOCK_SPEED: 268a0e911e0SIan Lepore error = spibus_set_clock(dev, *(uint32_t *)data); 269a0e911e0SIan Lepore break; 270a0e911e0SIan Lepore case SPIGENIOC_GET_SPI_MODE: 271197d784bSIan Lepore error = spibus_get_mode(dev, (uint32_t *)data); 272a0e911e0SIan Lepore break; 273a0e911e0SIan Lepore case SPIGENIOC_SET_SPI_MODE: 274a0e911e0SIan Lepore error = spibus_set_mode(dev, *(uint32_t *)data); 275d67fe28bSAdrian Chadd break; 276d67fe28bSAdrian Chadd default: 277a0e911e0SIan Lepore error = ENOTTY; 278a0e911e0SIan Lepore break; 279d67fe28bSAdrian Chadd } 280d67fe28bSAdrian Chadd return (error); 281d67fe28bSAdrian Chadd } 282d67fe28bSAdrian Chadd 2837a4f1915SIan Lepore static void 2847a4f1915SIan Lepore spigen_mmap_cleanup(void *arg) 2857a4f1915SIan Lepore { 2867a4f1915SIan Lepore struct spigen_mmap *mmap = arg; 2877a4f1915SIan Lepore 2887a4f1915SIan Lepore if (mmap->kvaddr != 0) 2897a4f1915SIan Lepore pmap_qremove(mmap->kvaddr, mmap->bufsize / PAGE_SIZE); 2907a4f1915SIan Lepore if (mmap->bufobj != NULL) 2917a4f1915SIan Lepore vm_object_deallocate(mmap->bufobj); 2927a4f1915SIan Lepore free(mmap, M_DEVBUF); 2937a4f1915SIan Lepore } 2947a4f1915SIan Lepore 295d67fe28bSAdrian Chadd static int 296d67fe28bSAdrian Chadd spigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 297d67fe28bSAdrian Chadd vm_size_t size, struct vm_object **object, int nprot) 298d67fe28bSAdrian Chadd { 2997a4f1915SIan Lepore struct spigen_mmap *mmap; 300d67fe28bSAdrian Chadd vm_page_t *m; 301d67fe28bSAdrian Chadd size_t n, pages; 3027a4f1915SIan Lepore int error; 303d67fe28bSAdrian Chadd 304d67fe28bSAdrian Chadd if (size == 0 || 305d67fe28bSAdrian Chadd (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE)) 306d67fe28bSAdrian Chadd != (PROT_READ | PROT_WRITE)) 307d67fe28bSAdrian Chadd return (EINVAL); 308d67fe28bSAdrian Chadd size = roundup2(size, PAGE_SIZE); 309d67fe28bSAdrian Chadd pages = size / PAGE_SIZE; 310d67fe28bSAdrian Chadd 3117a4f1915SIan Lepore if (devfs_get_cdevpriv((void **)&mmap) == 0) 312d67fe28bSAdrian Chadd return (EBUSY); 3137a4f1915SIan Lepore 3147a4f1915SIan Lepore mmap = malloc(sizeof(*mmap), M_DEVBUF, M_ZERO | M_WAITOK); 3157a4f1915SIan Lepore if ((mmap->kvaddr = kva_alloc(size)) == 0) { 3167a4f1915SIan Lepore spigen_mmap_cleanup(mmap); 3177a4f1915SIan Lepore return (ENOMEM); 318d67fe28bSAdrian Chadd } 3197a4f1915SIan Lepore mmap->bufsize = size; 3207a4f1915SIan Lepore mmap->bufobj = vm_pager_allocate(OBJT_PHYS, 0, size, nprot, 0, 3217a4f1915SIan Lepore curthread->td_ucred); 3227a4f1915SIan Lepore 323d67fe28bSAdrian Chadd m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK); 3247a4f1915SIan Lepore VM_OBJECT_WLOCK(mmap->bufobj); 3257a4f1915SIan Lepore vm_object_reference_locked(mmap->bufobj); // kernel and userland both 326d67fe28bSAdrian Chadd for (n = 0; n < pages; n++) { 3277a4f1915SIan Lepore m[n] = vm_page_grab(mmap->bufobj, n, 32891e31c3cSJeff Roberson VM_ALLOC_ZERO | VM_ALLOC_WIRED); 32991e31c3cSJeff Roberson vm_page_valid(m[n]); 33091e31c3cSJeff Roberson vm_page_xunbusy(m[n]); 331d67fe28bSAdrian Chadd } 3327a4f1915SIan Lepore VM_OBJECT_WUNLOCK(mmap->bufobj); 3337a4f1915SIan Lepore pmap_qenter(mmap->kvaddr, m, pages); 334d67fe28bSAdrian Chadd free(m, M_TEMP); 335d67fe28bSAdrian Chadd 3367a4f1915SIan Lepore if ((error = devfs_set_cdevpriv(mmap, spigen_mmap_cleanup)) != 0) { 3377a4f1915SIan Lepore /* Two threads were racing through this code; we lost. */ 3387a4f1915SIan Lepore spigen_mmap_cleanup(mmap); 3397a4f1915SIan Lepore return (error); 3407a4f1915SIan Lepore } 3417a4f1915SIan Lepore *offset = 0; 3427a4f1915SIan Lepore *object = mmap->bufobj; 3437a4f1915SIan Lepore 344d67fe28bSAdrian Chadd return (0); 345d67fe28bSAdrian Chadd } 346d67fe28bSAdrian Chadd 347d67fe28bSAdrian Chadd static int 348d67fe28bSAdrian Chadd spigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 349d67fe28bSAdrian Chadd { 350d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1; 351d67fe28bSAdrian Chadd struct spigen_softc *sc = device_get_softc(dev); 352d67fe28bSAdrian Chadd 353d67fe28bSAdrian Chadd mtx_lock(&sc->sc_mtx); 3547a4f1915SIan Lepore device_unbusy(sc->sc_dev); 355d67fe28bSAdrian Chadd mtx_unlock(&sc->sc_mtx); 356d67fe28bSAdrian Chadd return (0); 357d67fe28bSAdrian Chadd } 358d67fe28bSAdrian Chadd 359d67fe28bSAdrian Chadd static int 360d67fe28bSAdrian Chadd spigen_detach(device_t dev) 361d67fe28bSAdrian Chadd { 36250868fa6SOleksandr Tymoshenko struct spigen_softc *sc; 363d67fe28bSAdrian Chadd 36450868fa6SOleksandr Tymoshenko sc = device_get_softc(dev); 36550868fa6SOleksandr Tymoshenko 3661fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME 3671fcf4de0SIan Lepore if (sc->sc_adev) 3681fcf4de0SIan Lepore destroy_dev(sc->sc_adev); 3691fcf4de0SIan Lepore #endif 3701fcf4de0SIan Lepore 37150868fa6SOleksandr Tymoshenko if (sc->sc_cdev) 37250868fa6SOleksandr Tymoshenko destroy_dev(sc->sc_cdev); 37350868fa6SOleksandr Tymoshenko 3747a4f1915SIan Lepore mtx_destroy(&sc->sc_mtx); 3757a4f1915SIan Lepore 37650868fa6SOleksandr Tymoshenko return (0); 377d67fe28bSAdrian Chadd } 378d67fe28bSAdrian Chadd 379d67fe28bSAdrian Chadd static device_method_t spigen_methods[] = { 380d67fe28bSAdrian Chadd /* Device interface */ 381d67fe28bSAdrian Chadd DEVMETHOD(device_probe, spigen_probe), 382d67fe28bSAdrian Chadd DEVMETHOD(device_attach, spigen_attach), 383d67fe28bSAdrian Chadd DEVMETHOD(device_detach, spigen_detach), 384d67fe28bSAdrian Chadd { 0, 0 } 385d67fe28bSAdrian Chadd }; 386d67fe28bSAdrian Chadd 387d67fe28bSAdrian Chadd static driver_t spigen_driver = { 388d67fe28bSAdrian Chadd "spigen", 389d67fe28bSAdrian Chadd spigen_methods, 390d67fe28bSAdrian Chadd sizeof(struct spigen_softc), 391d67fe28bSAdrian Chadd }; 392d67fe28bSAdrian Chadd 393*372cb4d0SJohn Baldwin DRIVER_MODULE(spigen, spibus, spigen_driver, 0, 0); 394cdfebb9cSIan Lepore MODULE_DEPEND(spigen, spibus, 1, 1, 1); 3957a4f1915SIan Lepore #ifdef FDT 3967a4f1915SIan Lepore SIMPLEBUS_PNP_INFO(compat_data); 3977a4f1915SIan Lepore #endif 398