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>
26dac458e0SEmmanuel Vadot #include "opt_platform.h"
271fcf4de0SIan Lepore #include "opt_spi.h"
28dac458e0SEmmanuel Vadot
29d67fe28bSAdrian Chadd #include <sys/param.h>
30d67fe28bSAdrian Chadd #include <sys/systm.h>
31d67fe28bSAdrian Chadd #include <sys/bus.h>
32d67fe28bSAdrian Chadd #include <sys/conf.h>
33d67fe28bSAdrian Chadd #include <sys/kernel.h>
34d67fe28bSAdrian Chadd #include <sys/lock.h>
35d67fe28bSAdrian Chadd #include <sys/malloc.h>
36d67fe28bSAdrian Chadd #include <sys/mman.h>
37d67fe28bSAdrian Chadd #include <sys/mutex.h>
38d67fe28bSAdrian Chadd #include <sys/module.h>
39d67fe28bSAdrian Chadd #include <sys/proc.h>
40d67fe28bSAdrian Chadd #include <sys/rwlock.h>
41d67fe28bSAdrian Chadd #include <sys/spigenio.h>
42d67fe28bSAdrian Chadd #include <sys/types.h>
43d67fe28bSAdrian Chadd
44d67fe28bSAdrian Chadd #include <vm/vm.h>
45d67fe28bSAdrian Chadd #include <vm/vm_extern.h>
46d67fe28bSAdrian Chadd #include <vm/vm_object.h>
47d67fe28bSAdrian Chadd #include <vm/vm_page.h>
48d67fe28bSAdrian Chadd #include <vm/vm_pager.h>
49d67fe28bSAdrian Chadd
50d67fe28bSAdrian Chadd #include <dev/spibus/spi.h>
51a0e911e0SIan Lepore #include <dev/spibus/spibusvar.h>
52d67fe28bSAdrian Chadd
533b46d868SIan Lepore #ifdef FDT
543b46d868SIan Lepore #include <dev/ofw/ofw_bus_subr.h>
557a4f1915SIan Lepore
567a4f1915SIan Lepore static struct ofw_compat_data compat_data[] = {
577a4f1915SIan Lepore {"freebsd,spigen", true},
587a4f1915SIan Lepore {NULL, false}
597a4f1915SIan Lepore };
607a4f1915SIan Lepore
613b46d868SIan Lepore #endif
623b46d868SIan Lepore
63d67fe28bSAdrian Chadd #include "spibus_if.h"
64d67fe28bSAdrian Chadd
65d67fe28bSAdrian Chadd struct spigen_softc {
66d67fe28bSAdrian Chadd device_t sc_dev;
67d67fe28bSAdrian Chadd struct cdev *sc_cdev;
681fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME
691fcf4de0SIan Lepore struct cdev *sc_adev; /* alias device */
701fcf4de0SIan Lepore #endif
71d67fe28bSAdrian Chadd struct mtx sc_mtx;
727a4f1915SIan Lepore };
737a4f1915SIan Lepore
747a4f1915SIan Lepore struct spigen_mmap {
757a4f1915SIan Lepore vm_object_t bufobj;
767a4f1915SIan Lepore vm_offset_t kvaddr;
777a4f1915SIan Lepore size_t bufsize;
78d67fe28bSAdrian Chadd };
79d67fe28bSAdrian Chadd
80d67fe28bSAdrian Chadd static int
spigen_probe(device_t dev)81d67fe28bSAdrian Chadd spigen_probe(device_t dev)
82d67fe28bSAdrian Chadd {
833b46d868SIan Lepore int rv;
8432d74127SOleksandr Tymoshenko
85bc7b44aeSIan Lepore /*
86bc7b44aeSIan Lepore * By default we only bid to attach if specifically added by our parent
87bc7b44aeSIan Lepore * (usually via hint.spigen.#.at=busname). On FDT systems we bid as the
88bc7b44aeSIan Lepore * default driver based on being configured in the FDT data.
89bc7b44aeSIan Lepore */
903b46d868SIan Lepore rv = BUS_PROBE_NOWILDCARD;
91bc7b44aeSIan Lepore
92bc7b44aeSIan Lepore #ifdef FDT
93bc7b44aeSIan Lepore if (ofw_bus_status_okay(dev) &&
947a4f1915SIan Lepore ofw_bus_search_compatible(dev, compat_data)->ocd_data)
95bc7b44aeSIan Lepore rv = BUS_PROBE_DEFAULT;
963b46d868SIan Lepore #endif
97bc7b44aeSIan Lepore
98d67fe28bSAdrian Chadd device_set_desc(dev, "SPI Generic IO");
9932d74127SOleksandr Tymoshenko
1003b46d868SIan Lepore return (rv);
101d67fe28bSAdrian Chadd }
102d67fe28bSAdrian Chadd
103d67fe28bSAdrian Chadd static int spigen_open(struct cdev *, int, int, struct thread *);
104d67fe28bSAdrian Chadd static int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
105d67fe28bSAdrian Chadd static int spigen_close(struct cdev *, int, int, struct thread *);
106d67fe28bSAdrian Chadd static d_mmap_single_t spigen_mmap_single;
107d67fe28bSAdrian Chadd
108d67fe28bSAdrian Chadd static struct cdevsw spigen_cdevsw = {
109d67fe28bSAdrian Chadd .d_version = D_VERSION,
110d67fe28bSAdrian Chadd .d_name = "spigen",
111d67fe28bSAdrian Chadd .d_open = spigen_open,
112d67fe28bSAdrian Chadd .d_ioctl = spigen_ioctl,
113d67fe28bSAdrian Chadd .d_mmap_single = spigen_mmap_single,
114d67fe28bSAdrian Chadd .d_close = spigen_close
115d67fe28bSAdrian Chadd };
116d67fe28bSAdrian Chadd
117d67fe28bSAdrian Chadd static int
spigen_attach(device_t dev)118d67fe28bSAdrian Chadd spigen_attach(device_t dev)
119d67fe28bSAdrian Chadd {
120d67fe28bSAdrian Chadd struct spigen_softc *sc;
121d67fe28bSAdrian Chadd const int unit = device_get_unit(dev);
1221fcf4de0SIan Lepore int cs, res;
1231fcf4de0SIan Lepore struct make_dev_args mda;
1241fcf4de0SIan Lepore
1251fcf4de0SIan Lepore spibus_get_cs(dev, &cs);
1261fcf4de0SIan Lepore cs &= ~SPIBUS_CS_HIGH; /* trim 'cs high' bit */
127d67fe28bSAdrian Chadd
128d67fe28bSAdrian Chadd sc = device_get_softc(dev);
129d67fe28bSAdrian Chadd sc->sc_dev = dev;
1301fcf4de0SIan Lepore
131d67fe28bSAdrian Chadd mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
1321fcf4de0SIan Lepore
1331fcf4de0SIan Lepore make_dev_args_init(&mda);
1341fcf4de0SIan Lepore mda.mda_flags = MAKEDEV_WAITOK;
1351fcf4de0SIan Lepore mda.mda_devsw = &spigen_cdevsw;
1361fcf4de0SIan Lepore mda.mda_cr = NULL;
1371fcf4de0SIan Lepore mda.mda_uid = UID_ROOT;
1381fcf4de0SIan Lepore mda.mda_gid = GID_OPERATOR;
1391fcf4de0SIan Lepore mda.mda_mode = 0660;
1401fcf4de0SIan Lepore mda.mda_unit = unit;
1411fcf4de0SIan Lepore mda.mda_si_drv1 = dev;
1421fcf4de0SIan Lepore
1431fcf4de0SIan Lepore res = make_dev_s(&mda, &(sc->sc_cdev), "spigen%d.%d",
1441fcf4de0SIan Lepore device_get_unit(device_get_parent(dev)), cs);
1451fcf4de0SIan Lepore if (res) {
1461fcf4de0SIan Lepore return res;
1471fcf4de0SIan Lepore }
1481fcf4de0SIan Lepore
1491fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME
1501fcf4de0SIan Lepore res = make_dev_alias_p(0, &sc->sc_adev, sc->sc_cdev, "spigen%d", unit);
1511fcf4de0SIan Lepore if (res) {
1521fcf4de0SIan Lepore if (sc->sc_cdev) {
1531fcf4de0SIan Lepore destroy_dev(sc->sc_cdev);
1541fcf4de0SIan Lepore sc->sc_cdev = NULL;
1551fcf4de0SIan Lepore }
1561fcf4de0SIan Lepore return res;
1571fcf4de0SIan Lepore }
1581fcf4de0SIan Lepore #endif
1591fcf4de0SIan Lepore
160d67fe28bSAdrian Chadd return (0);
161d67fe28bSAdrian Chadd }
162d67fe28bSAdrian Chadd
163d67fe28bSAdrian Chadd static int
spigen_open(struct cdev * cdev,int oflags,int devtype,struct thread * td)16450868fa6SOleksandr Tymoshenko spigen_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
165d67fe28bSAdrian Chadd {
16650868fa6SOleksandr Tymoshenko device_t dev;
16750868fa6SOleksandr Tymoshenko struct spigen_softc *sc;
168d67fe28bSAdrian Chadd
16950868fa6SOleksandr Tymoshenko dev = cdev->si_drv1;
17050868fa6SOleksandr Tymoshenko sc = device_get_softc(dev);
17150868fa6SOleksandr Tymoshenko
17250868fa6SOleksandr Tymoshenko mtx_lock(&sc->sc_mtx);
1737a4f1915SIan Lepore device_busy(sc->sc_dev);
17450868fa6SOleksandr Tymoshenko mtx_unlock(&sc->sc_mtx);
17550868fa6SOleksandr Tymoshenko
1767a4f1915SIan Lepore return (0);
177d67fe28bSAdrian Chadd }
178d67fe28bSAdrian Chadd
179d67fe28bSAdrian Chadd static int
spigen_transfer(struct cdev * cdev,struct spigen_transfer * st)180d67fe28bSAdrian Chadd spigen_transfer(struct cdev *cdev, struct spigen_transfer *st)
181d67fe28bSAdrian Chadd {
182d67fe28bSAdrian Chadd struct spi_command transfer = SPI_COMMAND_INITIALIZER;
183d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1;
184d67fe28bSAdrian Chadd int error = 0;
185d67fe28bSAdrian Chadd
186d67fe28bSAdrian Chadd #if 0
187d67fe28bSAdrian Chadd device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base,
188d67fe28bSAdrian Chadd st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len);
189d67fe28bSAdrian Chadd #endif
1907a4f1915SIan Lepore
1917a4f1915SIan Lepore if (st->st_command.iov_len == 0)
1927a4f1915SIan Lepore return (EINVAL);
1937a4f1915SIan Lepore
194d67fe28bSAdrian Chadd transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len,
195d67fe28bSAdrian Chadd M_DEVBUF, M_WAITOK);
1963c43a826SOleksandr Tymoshenko if (st->st_data.iov_len > 0) {
197d67fe28bSAdrian Chadd transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len,
198d67fe28bSAdrian Chadd M_DEVBUF, M_WAITOK);
1993c43a826SOleksandr Tymoshenko }
2003c43a826SOleksandr Tymoshenko else
2013c43a826SOleksandr Tymoshenko transfer.tx_data = transfer.rx_data = NULL;
202d67fe28bSAdrian Chadd
203d67fe28bSAdrian Chadd error = copyin(st->st_command.iov_base, transfer.tx_cmd,
204d67fe28bSAdrian Chadd transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len);
2053c43a826SOleksandr Tymoshenko if ((error == 0) && (st->st_data.iov_len > 0))
206d67fe28bSAdrian Chadd error = copyin(st->st_data.iov_base, transfer.tx_data,
207d67fe28bSAdrian Chadd transfer.tx_data_sz = transfer.rx_data_sz =
208d67fe28bSAdrian Chadd st->st_data.iov_len);
209d67fe28bSAdrian Chadd if (error == 0)
210d67fe28bSAdrian Chadd error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
211d67fe28bSAdrian Chadd if (error == 0) {
212d67fe28bSAdrian Chadd error = copyout(transfer.rx_cmd, st->st_command.iov_base,
213d67fe28bSAdrian Chadd transfer.rx_cmd_sz);
2143c43a826SOleksandr Tymoshenko if ((error == 0) && (st->st_data.iov_len > 0))
215d67fe28bSAdrian Chadd error = copyout(transfer.rx_data, st->st_data.iov_base,
216d67fe28bSAdrian Chadd transfer.rx_data_sz);
217d67fe28bSAdrian Chadd }
218d67fe28bSAdrian Chadd
219d67fe28bSAdrian Chadd free(transfer.tx_cmd, M_DEVBUF);
220d67fe28bSAdrian Chadd free(transfer.tx_data, M_DEVBUF);
221d67fe28bSAdrian Chadd return (error);
222d67fe28bSAdrian Chadd }
223d67fe28bSAdrian Chadd
224d67fe28bSAdrian Chadd static int
spigen_transfer_mmapped(struct cdev * cdev,struct spigen_transfer_mmapped * stm)225d67fe28bSAdrian Chadd spigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm)
226d67fe28bSAdrian Chadd {
227d67fe28bSAdrian Chadd struct spi_command transfer = SPI_COMMAND_INITIALIZER;
228d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1;
2297a4f1915SIan Lepore struct spigen_mmap *mmap;
2307a4f1915SIan Lepore int error;
231d67fe28bSAdrian Chadd
2327a4f1915SIan Lepore if ((error = devfs_get_cdevpriv((void **)&mmap)) != 0)
233d67fe28bSAdrian Chadd return (error);
234d67fe28bSAdrian Chadd
2357a4f1915SIan Lepore if (mmap->bufsize < stm->stm_command_length + stm->stm_data_length)
2367a4f1915SIan Lepore return (E2BIG);
2377a4f1915SIan Lepore
2387a4f1915SIan Lepore transfer.tx_cmd = transfer.rx_cmd = (void *)((uintptr_t)mmap->kvaddr);
239d67fe28bSAdrian Chadd transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length;
240d67fe28bSAdrian Chadd transfer.tx_data = transfer.rx_data =
2417a4f1915SIan Lepore (void *)((uintptr_t)mmap->kvaddr + stm->stm_command_length);
242d67fe28bSAdrian Chadd transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length;
243d67fe28bSAdrian Chadd error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
244d67fe28bSAdrian Chadd
245d67fe28bSAdrian Chadd return (error);
246d67fe28bSAdrian Chadd }
247d67fe28bSAdrian Chadd
248d67fe28bSAdrian Chadd static int
spigen_ioctl(struct cdev * cdev,u_long cmd,caddr_t data,int fflag,struct thread * td)249d67fe28bSAdrian Chadd spigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
250d67fe28bSAdrian Chadd struct thread *td)
251d67fe28bSAdrian Chadd {
252d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1;
253d67fe28bSAdrian Chadd int error;
254d67fe28bSAdrian Chadd
255d67fe28bSAdrian Chadd switch (cmd) {
256d67fe28bSAdrian Chadd case SPIGENIOC_TRANSFER:
257d67fe28bSAdrian Chadd error = spigen_transfer(cdev, (struct spigen_transfer *)data);
258d67fe28bSAdrian Chadd break;
259d67fe28bSAdrian Chadd case SPIGENIOC_TRANSFER_MMAPPED:
260d67fe28bSAdrian Chadd error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data);
261d67fe28bSAdrian Chadd break;
262d67fe28bSAdrian Chadd case SPIGENIOC_GET_CLOCK_SPEED:
263197d784bSIan Lepore error = spibus_get_clock(dev, (uint32_t *)data);
264d67fe28bSAdrian Chadd break;
265d67fe28bSAdrian Chadd case SPIGENIOC_SET_CLOCK_SPEED:
266a0e911e0SIan Lepore error = spibus_set_clock(dev, *(uint32_t *)data);
267a0e911e0SIan Lepore break;
268a0e911e0SIan Lepore case SPIGENIOC_GET_SPI_MODE:
269197d784bSIan Lepore error = spibus_get_mode(dev, (uint32_t *)data);
270a0e911e0SIan Lepore break;
271a0e911e0SIan Lepore case SPIGENIOC_SET_SPI_MODE:
272a0e911e0SIan Lepore error = spibus_set_mode(dev, *(uint32_t *)data);
273d67fe28bSAdrian Chadd break;
274d67fe28bSAdrian Chadd default:
275a0e911e0SIan Lepore error = ENOTTY;
276a0e911e0SIan Lepore break;
277d67fe28bSAdrian Chadd }
278d67fe28bSAdrian Chadd return (error);
279d67fe28bSAdrian Chadd }
280d67fe28bSAdrian Chadd
2817a4f1915SIan Lepore static void
spigen_mmap_cleanup(void * arg)2827a4f1915SIan Lepore spigen_mmap_cleanup(void *arg)
2837a4f1915SIan Lepore {
2847a4f1915SIan Lepore struct spigen_mmap *mmap = arg;
2857a4f1915SIan Lepore
2867a4f1915SIan Lepore if (mmap->kvaddr != 0)
2877a4f1915SIan Lepore pmap_qremove(mmap->kvaddr, mmap->bufsize / PAGE_SIZE);
2887a4f1915SIan Lepore if (mmap->bufobj != NULL)
2897a4f1915SIan Lepore vm_object_deallocate(mmap->bufobj);
2907a4f1915SIan Lepore free(mmap, M_DEVBUF);
2917a4f1915SIan Lepore }
2927a4f1915SIan Lepore
293d67fe28bSAdrian Chadd static int
spigen_mmap_single(struct cdev * cdev,vm_ooffset_t * offset,vm_size_t size,struct vm_object ** object,int nprot)294d67fe28bSAdrian Chadd spigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset,
295d67fe28bSAdrian Chadd vm_size_t size, struct vm_object **object, int nprot)
296d67fe28bSAdrian Chadd {
2977a4f1915SIan Lepore struct spigen_mmap *mmap;
298d67fe28bSAdrian Chadd vm_page_t *m;
299d67fe28bSAdrian Chadd size_t n, pages;
3007a4f1915SIan Lepore int error;
301d67fe28bSAdrian Chadd
302d67fe28bSAdrian Chadd if (size == 0 ||
303d67fe28bSAdrian Chadd (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE))
304d67fe28bSAdrian Chadd != (PROT_READ | PROT_WRITE))
305d67fe28bSAdrian Chadd return (EINVAL);
306d67fe28bSAdrian Chadd size = roundup2(size, PAGE_SIZE);
307d67fe28bSAdrian Chadd pages = size / PAGE_SIZE;
308d67fe28bSAdrian Chadd
3097a4f1915SIan Lepore if (devfs_get_cdevpriv((void **)&mmap) == 0)
310d67fe28bSAdrian Chadd return (EBUSY);
3117a4f1915SIan Lepore
3127a4f1915SIan Lepore mmap = malloc(sizeof(*mmap), M_DEVBUF, M_ZERO | M_WAITOK);
3137a4f1915SIan Lepore if ((mmap->kvaddr = kva_alloc(size)) == 0) {
3147a4f1915SIan Lepore spigen_mmap_cleanup(mmap);
3157a4f1915SIan Lepore return (ENOMEM);
316d67fe28bSAdrian Chadd }
3177a4f1915SIan Lepore mmap->bufsize = size;
3187a4f1915SIan Lepore mmap->bufobj = vm_pager_allocate(OBJT_PHYS, 0, size, nprot, 0,
3197a4f1915SIan Lepore curthread->td_ucred);
3207a4f1915SIan Lepore
321d67fe28bSAdrian Chadd m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK);
3227a4f1915SIan Lepore VM_OBJECT_WLOCK(mmap->bufobj);
3237a4f1915SIan Lepore vm_object_reference_locked(mmap->bufobj); // kernel and userland both
324d67fe28bSAdrian Chadd for (n = 0; n < pages; n++) {
3257a4f1915SIan Lepore m[n] = vm_page_grab(mmap->bufobj, n,
32691e31c3cSJeff Roberson VM_ALLOC_ZERO | VM_ALLOC_WIRED);
32791e31c3cSJeff Roberson vm_page_valid(m[n]);
32891e31c3cSJeff Roberson vm_page_xunbusy(m[n]);
329d67fe28bSAdrian Chadd }
3307a4f1915SIan Lepore VM_OBJECT_WUNLOCK(mmap->bufobj);
3317a4f1915SIan Lepore pmap_qenter(mmap->kvaddr, m, pages);
332d67fe28bSAdrian Chadd free(m, M_TEMP);
333d67fe28bSAdrian Chadd
3347a4f1915SIan Lepore if ((error = devfs_set_cdevpriv(mmap, spigen_mmap_cleanup)) != 0) {
3357a4f1915SIan Lepore /* Two threads were racing through this code; we lost. */
3367a4f1915SIan Lepore spigen_mmap_cleanup(mmap);
3377a4f1915SIan Lepore return (error);
3387a4f1915SIan Lepore }
3397a4f1915SIan Lepore *offset = 0;
3407a4f1915SIan Lepore *object = mmap->bufobj;
3417a4f1915SIan Lepore
342d67fe28bSAdrian Chadd return (0);
343d67fe28bSAdrian Chadd }
344d67fe28bSAdrian Chadd
345d67fe28bSAdrian Chadd static int
spigen_close(struct cdev * cdev,int fflag,int devtype,struct thread * td)346d67fe28bSAdrian Chadd spigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
347d67fe28bSAdrian Chadd {
348d67fe28bSAdrian Chadd device_t dev = cdev->si_drv1;
349d67fe28bSAdrian Chadd struct spigen_softc *sc = device_get_softc(dev);
350d67fe28bSAdrian Chadd
351d67fe28bSAdrian Chadd mtx_lock(&sc->sc_mtx);
3527a4f1915SIan Lepore device_unbusy(sc->sc_dev);
353d67fe28bSAdrian Chadd mtx_unlock(&sc->sc_mtx);
354d67fe28bSAdrian Chadd return (0);
355d67fe28bSAdrian Chadd }
356d67fe28bSAdrian Chadd
357d67fe28bSAdrian Chadd static int
spigen_detach(device_t dev)358d67fe28bSAdrian Chadd spigen_detach(device_t dev)
359d67fe28bSAdrian Chadd {
36050868fa6SOleksandr Tymoshenko struct spigen_softc *sc;
361d67fe28bSAdrian Chadd
36250868fa6SOleksandr Tymoshenko sc = device_get_softc(dev);
36350868fa6SOleksandr Tymoshenko
3641fcf4de0SIan Lepore #ifdef SPIGEN_LEGACY_CDEVNAME
3651fcf4de0SIan Lepore if (sc->sc_adev)
3661fcf4de0SIan Lepore destroy_dev(sc->sc_adev);
3671fcf4de0SIan Lepore #endif
3681fcf4de0SIan Lepore
36950868fa6SOleksandr Tymoshenko if (sc->sc_cdev)
37050868fa6SOleksandr Tymoshenko destroy_dev(sc->sc_cdev);
37150868fa6SOleksandr Tymoshenko
3727a4f1915SIan Lepore mtx_destroy(&sc->sc_mtx);
3737a4f1915SIan Lepore
37450868fa6SOleksandr Tymoshenko return (0);
375d67fe28bSAdrian Chadd }
376d67fe28bSAdrian Chadd
377d67fe28bSAdrian Chadd static device_method_t spigen_methods[] = {
378d67fe28bSAdrian Chadd /* Device interface */
379d67fe28bSAdrian Chadd DEVMETHOD(device_probe, spigen_probe),
380d67fe28bSAdrian Chadd DEVMETHOD(device_attach, spigen_attach),
381d67fe28bSAdrian Chadd DEVMETHOD(device_detach, spigen_detach),
382d67fe28bSAdrian Chadd { 0, 0 }
383d67fe28bSAdrian Chadd };
384d67fe28bSAdrian Chadd
385d67fe28bSAdrian Chadd static driver_t spigen_driver = {
386d67fe28bSAdrian Chadd "spigen",
387d67fe28bSAdrian Chadd spigen_methods,
388d67fe28bSAdrian Chadd sizeof(struct spigen_softc),
389d67fe28bSAdrian Chadd };
390d67fe28bSAdrian Chadd
391*372cb4d0SJohn Baldwin DRIVER_MODULE(spigen, spibus, spigen_driver, 0, 0);
392cdfebb9cSIan Lepore MODULE_DEPEND(spigen, spibus, 1, 1, 1);
3937a4f1915SIan Lepore #ifdef FDT
3947a4f1915SIan Lepore SIMPLEBUS_PNP_INFO(compat_data);
3957a4f1915SIan Lepore #endif
396