xref: /freebsd/sys/dev/spibus/spigen.c (revision 372cb4d0cc317e841a1f8f6582851160e078d16b)
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