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