xref: /freebsd/sys/dev/altera/avgen/altera_avgen_nexus.c (revision 86f4a437c9d91ccf32578fee4eece737e98e2c22)
1*86f4a437SRobert Watson /*-
2*86f4a437SRobert Watson  * Copyright (c) 2012 Robert N. M. Watson
3*86f4a437SRobert Watson  * All rights reserved.
4*86f4a437SRobert Watson  *
5*86f4a437SRobert Watson  * This software was developed by SRI International and the University of
6*86f4a437SRobert Watson  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7*86f4a437SRobert Watson  * ("CTSRD"), as part of the DARPA CRASH research programme.
8*86f4a437SRobert Watson  *
9*86f4a437SRobert Watson  * Redistribution and use in source and binary forms, with or without
10*86f4a437SRobert Watson  * modification, are permitted provided that the following conditions
11*86f4a437SRobert Watson  * are met:
12*86f4a437SRobert Watson  * 1. Redistributions of source code must retain the above copyright
13*86f4a437SRobert Watson  *    notice, this list of conditions and the following disclaimer.
14*86f4a437SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
15*86f4a437SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
16*86f4a437SRobert Watson  *    documentation and/or other materials provided with the distribution.
17*86f4a437SRobert Watson  *
18*86f4a437SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*86f4a437SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*86f4a437SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*86f4a437SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*86f4a437SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*86f4a437SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*86f4a437SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*86f4a437SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*86f4a437SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*86f4a437SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*86f4a437SRobert Watson  * SUCH DAMAGE.
29*86f4a437SRobert Watson  */
30*86f4a437SRobert Watson 
31*86f4a437SRobert Watson #include <sys/cdefs.h>
32*86f4a437SRobert Watson __FBSDID("$FreeBSD$");
33*86f4a437SRobert Watson 
34*86f4a437SRobert Watson #include <sys/param.h>
35*86f4a437SRobert Watson #include <sys/bus.h>
36*86f4a437SRobert Watson #include <sys/condvar.h>
37*86f4a437SRobert Watson #include <sys/conf.h>
38*86f4a437SRobert Watson #include <sys/kernel.h>
39*86f4a437SRobert Watson #include <sys/lock.h>
40*86f4a437SRobert Watson #include <sys/malloc.h>
41*86f4a437SRobert Watson #include <sys/module.h>
42*86f4a437SRobert Watson #include <sys/mutex.h>
43*86f4a437SRobert Watson #include <sys/rman.h>
44*86f4a437SRobert Watson #include <sys/stat.h>
45*86f4a437SRobert Watson #include <sys/systm.h>
46*86f4a437SRobert Watson #include <sys/uio.h>
47*86f4a437SRobert Watson 
48*86f4a437SRobert Watson #include <machine/bus.h>
49*86f4a437SRobert Watson #include <machine/resource.h>
50*86f4a437SRobert Watson #include <machine/vm.h>
51*86f4a437SRobert Watson 
52*86f4a437SRobert Watson #include <vm/vm.h>
53*86f4a437SRobert Watson 
54*86f4a437SRobert Watson #include <dev/altera/avgen/altera_avgen.h>
55*86f4a437SRobert Watson 
56*86f4a437SRobert Watson /*
57*86f4a437SRobert Watson  * Generic device driver for allowing read(), write(), and mmap() on
58*86f4a437SRobert Watson  * memory-mapped, Avalon-attached devices.  There is no actual dependence on
59*86f4a437SRobert Watson  * Avalon, so conceivably this should just be soc_dev or similar, since many
60*86f4a437SRobert Watson  * system-on-chip bus environments would work fine with the same code.
61*86f4a437SRobert Watson  */
62*86f4a437SRobert Watson 
63*86f4a437SRobert Watson static d_mmap_t altera_avgen_mmap;
64*86f4a437SRobert Watson static d_read_t altera_avgen_read;
65*86f4a437SRobert Watson static d_write_t altera_avgen_write;
66*86f4a437SRobert Watson 
67*86f4a437SRobert Watson static struct cdevsw avg_cdevsw = {
68*86f4a437SRobert Watson 	.d_version =	D_VERSION,
69*86f4a437SRobert Watson 	.d_mmap =	altera_avgen_mmap,
70*86f4a437SRobert Watson 	.d_read =	altera_avgen_read,
71*86f4a437SRobert Watson 	.d_write =	altera_avgen_write,
72*86f4a437SRobert Watson 	.d_name =	"altera_avgen",
73*86f4a437SRobert Watson };
74*86f4a437SRobert Watson 
75*86f4a437SRobert Watson static int
76*86f4a437SRobert Watson altera_avgen_read(struct cdev *dev, struct uio *uio, int flag)
77*86f4a437SRobert Watson {
78*86f4a437SRobert Watson 	struct altera_avgen_softc *sc;
79*86f4a437SRobert Watson 	u_long offset, size;
80*86f4a437SRobert Watson #ifdef NOTYET
81*86f4a437SRobert Watson 	uint64_t v8;
82*86f4a437SRobert Watson #endif
83*86f4a437SRobert Watson 	uint32_t v4;
84*86f4a437SRobert Watson 	uint16_t v2;
85*86f4a437SRobert Watson 	uint8_t v1;
86*86f4a437SRobert Watson 	u_int width;
87*86f4a437SRobert Watson 	int error;
88*86f4a437SRobert Watson 
89*86f4a437SRobert Watson 	sc = dev->si_drv1;
90*86f4a437SRobert Watson 	if ((sc->avg_flags & ALTERA_AVALON_FLAG_READ) == 0)
91*86f4a437SRobert Watson 		return (EACCES);
92*86f4a437SRobert Watson 	width = sc->avg_width;
93*86f4a437SRobert Watson 	if (uio->uio_offset < 0 || uio->uio_offset % width != 0 ||
94*86f4a437SRobert Watson 	    uio->uio_resid % width != 0)
95*86f4a437SRobert Watson 		return (ENODEV);
96*86f4a437SRobert Watson 	size = rman_get_size(sc->avg_res);
97*86f4a437SRobert Watson 	if ((uio->uio_offset + uio->uio_resid < 0) ||
98*86f4a437SRobert Watson 	    (uio->uio_offset + uio->uio_resid > size))
99*86f4a437SRobert Watson 		return (ENODEV);
100*86f4a437SRobert Watson 	while (uio->uio_resid > 0) {
101*86f4a437SRobert Watson 		offset = uio->uio_offset;
102*86f4a437SRobert Watson 		if (offset + width > size)
103*86f4a437SRobert Watson 			return (ENODEV);
104*86f4a437SRobert Watson 		switch (width) {
105*86f4a437SRobert Watson 		case 1:
106*86f4a437SRobert Watson 			v1 = bus_read_1(sc->avg_res, offset);
107*86f4a437SRobert Watson 			error = uiomove(&v1, sizeof(v1), uio);
108*86f4a437SRobert Watson 			break;
109*86f4a437SRobert Watson 
110*86f4a437SRobert Watson 		case 2:
111*86f4a437SRobert Watson 			v2 = bus_read_2(sc->avg_res, offset);
112*86f4a437SRobert Watson 			error = uiomove(&v2, sizeof(v2), uio);
113*86f4a437SRobert Watson 			break;
114*86f4a437SRobert Watson 
115*86f4a437SRobert Watson 		case 4:
116*86f4a437SRobert Watson 			v4 = bus_read_4(sc->avg_res, offset);
117*86f4a437SRobert Watson 			error = uiomove(&v4, sizeof(v4), uio);
118*86f4a437SRobert Watson 			break;
119*86f4a437SRobert Watson 
120*86f4a437SRobert Watson #ifdef NOTYET
121*86f4a437SRobert Watson 		case 8:
122*86f4a437SRobert Watson 			v8 = bus_read_8(sc->avg_res, offset);
123*86f4a437SRobert Watson 			error = uiomove(&v8, sizeof(v8), uio);
124*86f4a437SRobert Watson 			break;
125*86f4a437SRobert Watson 
126*86f4a437SRobert Watson #endif
127*86f4a437SRobert Watson 
128*86f4a437SRobert Watson 		default:
129*86f4a437SRobert Watson 			panic("%s: unexpected widthment %u", __func__, width);
130*86f4a437SRobert Watson 		}
131*86f4a437SRobert Watson 		if (error)
132*86f4a437SRobert Watson 			return (error);
133*86f4a437SRobert Watson 	}
134*86f4a437SRobert Watson 	return (0);
135*86f4a437SRobert Watson }
136*86f4a437SRobert Watson 
137*86f4a437SRobert Watson static int
138*86f4a437SRobert Watson altera_avgen_write(struct cdev *dev, struct uio *uio, int flag)
139*86f4a437SRobert Watson {
140*86f4a437SRobert Watson 	struct altera_avgen_softc *sc;
141*86f4a437SRobert Watson 	u_long offset, size;
142*86f4a437SRobert Watson #ifdef NOTYET
143*86f4a437SRobert Watson 	uint64_t v8;
144*86f4a437SRobert Watson #endif
145*86f4a437SRobert Watson 	uint32_t v4;
146*86f4a437SRobert Watson 	uint16_t v2;
147*86f4a437SRobert Watson 	uint8_t v1;
148*86f4a437SRobert Watson 	u_int width;
149*86f4a437SRobert Watson 	int error;
150*86f4a437SRobert Watson 
151*86f4a437SRobert Watson 	sc = dev->si_drv1;
152*86f4a437SRobert Watson 	if ((sc->avg_flags & ALTERA_AVALON_FLAG_WRITE) == 0)
153*86f4a437SRobert Watson 		return (EACCES);
154*86f4a437SRobert Watson 	width = sc->avg_width;
155*86f4a437SRobert Watson 	if (uio->uio_offset < 0 || uio->uio_offset % width != 0 ||
156*86f4a437SRobert Watson 	    uio->uio_resid % width != 0)
157*86f4a437SRobert Watson 		return (ENODEV);
158*86f4a437SRobert Watson 	size = rman_get_size(sc->avg_res);
159*86f4a437SRobert Watson 	while (uio->uio_resid > 0) {
160*86f4a437SRobert Watson 		offset = uio->uio_offset;
161*86f4a437SRobert Watson 		if (offset + width > size)
162*86f4a437SRobert Watson 			return (ENODEV);
163*86f4a437SRobert Watson 		switch (width) {
164*86f4a437SRobert Watson 		case 1:
165*86f4a437SRobert Watson 			error = uiomove(&v1, sizeof(v1), uio);
166*86f4a437SRobert Watson 			if (error)
167*86f4a437SRobert Watson 				return (error);
168*86f4a437SRobert Watson 			bus_write_1(sc->avg_res, offset, v1);
169*86f4a437SRobert Watson 			break;
170*86f4a437SRobert Watson 
171*86f4a437SRobert Watson 		case 2:
172*86f4a437SRobert Watson 			error = uiomove(&v2, sizeof(v2), uio);
173*86f4a437SRobert Watson 			if (error)
174*86f4a437SRobert Watson 				return (error);
175*86f4a437SRobert Watson 			bus_write_2(sc->avg_res, offset, v2);
176*86f4a437SRobert Watson 			break;
177*86f4a437SRobert Watson 
178*86f4a437SRobert Watson 		case 4:
179*86f4a437SRobert Watson 			error = uiomove(&v4, sizeof(v4), uio);
180*86f4a437SRobert Watson 			if (error)
181*86f4a437SRobert Watson 				return (error);
182*86f4a437SRobert Watson 			bus_write_4(sc->avg_res, offset, v4);
183*86f4a437SRobert Watson 			break;
184*86f4a437SRobert Watson 
185*86f4a437SRobert Watson #ifdef NOTYET
186*86f4a437SRobert Watson 		case 8:
187*86f4a437SRobert Watson 			error = uiomove(&v8, sizeof(v8), uio);
188*86f4a437SRobert Watson 			if (error)
189*86f4a437SRobert Watson 				return (error);
190*86f4a437SRobert Watson 			bus_write_8(sc->avg_res, offset, v8);
191*86f4a437SRobert Watson 			break;
192*86f4a437SRobert Watson #endif
193*86f4a437SRobert Watson 
194*86f4a437SRobert Watson 		default:
195*86f4a437SRobert Watson 			panic("%s: unexpected width %u", __func__, width);
196*86f4a437SRobert Watson 		}
197*86f4a437SRobert Watson 	}
198*86f4a437SRobert Watson 	return (0);
199*86f4a437SRobert Watson }
200*86f4a437SRobert Watson 
201*86f4a437SRobert Watson static int
202*86f4a437SRobert Watson altera_avgen_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
203*86f4a437SRobert Watson     int nprot, vm_memattr_t *memattr)
204*86f4a437SRobert Watson {
205*86f4a437SRobert Watson 	struct altera_avgen_softc *sc;
206*86f4a437SRobert Watson 
207*86f4a437SRobert Watson 	sc = dev->si_drv1;
208*86f4a437SRobert Watson 	if (nprot & VM_PROT_READ) {
209*86f4a437SRobert Watson 		if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_READ) == 0)
210*86f4a437SRobert Watson 			return (EACCES);
211*86f4a437SRobert Watson 	}
212*86f4a437SRobert Watson 	if (nprot & VM_PROT_WRITE) {
213*86f4a437SRobert Watson 		if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_WRITE) == 0)
214*86f4a437SRobert Watson 			return (EACCES);
215*86f4a437SRobert Watson 	}
216*86f4a437SRobert Watson 	if (nprot & VM_PROT_EXECUTE) {
217*86f4a437SRobert Watson 		if ((sc->avg_flags & ALTERA_AVALON_FLAG_MMAP_EXEC) == 0)
218*86f4a437SRobert Watson 			return (EACCES);
219*86f4a437SRobert Watson 	}
220*86f4a437SRobert Watson 	if (trunc_page(offset) == offset &&
221*86f4a437SRobert Watson 	    rman_get_size(sc->avg_res) >= offset + PAGE_SIZE) {
222*86f4a437SRobert Watson 		*paddr = rman_get_start(sc->avg_res) + offset;
223*86f4a437SRobert Watson 		*memattr = VM_MEMATTR_UNCACHEABLE;
224*86f4a437SRobert Watson 	} else
225*86f4a437SRobert Watson 		return (ENODEV);
226*86f4a437SRobert Watson 	return (0);
227*86f4a437SRobert Watson }
228*86f4a437SRobert Watson 
229*86f4a437SRobert Watson static int
230*86f4a437SRobert Watson altera_avgen_nexus_probe(device_t dev)
231*86f4a437SRobert Watson {
232*86f4a437SRobert Watson 
233*86f4a437SRobert Watson 	device_set_desc(dev, "Generic Altera Avalon device attachment");
234*86f4a437SRobert Watson 	return (BUS_PROBE_DEFAULT);
235*86f4a437SRobert Watson }
236*86f4a437SRobert Watson 
237*86f4a437SRobert Watson static int
238*86f4a437SRobert Watson altera_avgen_process_options(struct altera_avgen_softc *sc,
239*86f4a437SRobert Watson     const char *str_fileio, const char *str_mmapio, const char *str_devname,
240*86f4a437SRobert Watson     int devunit)
241*86f4a437SRobert Watson {
242*86f4a437SRobert Watson 	const char *cp;
243*86f4a437SRobert Watson 	device_t dev = sc->avg_dev;
244*86f4a437SRobert Watson 
245*86f4a437SRobert Watson 	/*
246*86f4a437SRobert Watson 	 * Check for valid combinations of options.
247*86f4a437SRobert Watson 	 */
248*86f4a437SRobert Watson 	if (str_fileio == NULL && str_mmapio == NULL) {
249*86f4a437SRobert Watson 		device_printf(dev,
250*86f4a437SRobert Watson 		    "at least one of %s or %s must be specified\n",
251*86f4a437SRobert Watson 		    ALTERA_AVALON_STR_FILEIO, ALTERA_AVALON_STR_MMAPIO);
252*86f4a437SRobert Watson 		return (ENXIO);
253*86f4a437SRobert Watson 	}
254*86f4a437SRobert Watson 	if (str_devname == NULL && devunit != -1) {
255*86f4a437SRobert Watson 		device_printf(dev, "%s requires %s be specified\n",
256*86f4a437SRobert Watson 		    ALTERA_AVALON_STR_DEVUNIT, ALTERA_AVALON_STR_DEVNAME);
257*86f4a437SRobert Watson 		return (ENXIO);
258*86f4a437SRobert Watson 	}
259*86f4a437SRobert Watson 
260*86f4a437SRobert Watson 	/*
261*86f4a437SRobert Watson 	 * Extract, digest, and save values.
262*86f4a437SRobert Watson 	 */
263*86f4a437SRobert Watson 	switch (sc->avg_width) {
264*86f4a437SRobert Watson 	case 1:
265*86f4a437SRobert Watson 	case 2:
266*86f4a437SRobert Watson 	case 4:
267*86f4a437SRobert Watson #ifdef NOTYET
268*86f4a437SRobert Watson 	case 8:
269*86f4a437SRobert Watson #endif
270*86f4a437SRobert Watson 		break;
271*86f4a437SRobert Watson 
272*86f4a437SRobert Watson 	default:
273*86f4a437SRobert Watson 		device_printf(dev, "%s unsupported value %u\n",
274*86f4a437SRobert Watson 		    ALTERA_AVALON_STR_WIDTH, sc->avg_width);
275*86f4a437SRobert Watson 		return (ENXIO);
276*86f4a437SRobert Watson 	}
277*86f4a437SRobert Watson 	sc->avg_flags = 0;
278*86f4a437SRobert Watson 	if (str_fileio != NULL) {
279*86f4a437SRobert Watson 		for (cp = str_fileio; *cp != '\0'; cp++) {
280*86f4a437SRobert Watson 			switch (*cp) {
281*86f4a437SRobert Watson 			case ALTERA_AVALON_CHAR_READ:
282*86f4a437SRobert Watson 				sc->avg_flags |= ALTERA_AVALON_FLAG_READ;
283*86f4a437SRobert Watson 				break;
284*86f4a437SRobert Watson 
285*86f4a437SRobert Watson 			case ALTERA_AVALON_CHAR_WRITE:
286*86f4a437SRobert Watson 				sc->avg_flags |= ALTERA_AVALON_FLAG_WRITE;
287*86f4a437SRobert Watson 				break;
288*86f4a437SRobert Watson 
289*86f4a437SRobert Watson 			default:
290*86f4a437SRobert Watson 				device_printf(dev,
291*86f4a437SRobert Watson 				    "invalid %s character %c\n",
292*86f4a437SRobert Watson 				    ALTERA_AVALON_STR_FILEIO, *cp);
293*86f4a437SRobert Watson 				return (ENXIO);
294*86f4a437SRobert Watson 			}
295*86f4a437SRobert Watson 		}
296*86f4a437SRobert Watson 	}
297*86f4a437SRobert Watson 	if (str_mmapio != NULL) {
298*86f4a437SRobert Watson 		for (cp = str_mmapio; *cp != '\0'; cp++) {
299*86f4a437SRobert Watson 			switch (*cp) {
300*86f4a437SRobert Watson 			case ALTERA_AVALON_CHAR_READ:
301*86f4a437SRobert Watson 				sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_READ;
302*86f4a437SRobert Watson 				break;
303*86f4a437SRobert Watson 
304*86f4a437SRobert Watson 			case ALTERA_AVALON_CHAR_WRITE:
305*86f4a437SRobert Watson 				sc->avg_flags |=
306*86f4a437SRobert Watson 				    ALTERA_AVALON_FLAG_MMAP_WRITE;
307*86f4a437SRobert Watson 				break;
308*86f4a437SRobert Watson 
309*86f4a437SRobert Watson 			case ALTERA_AVALON_CHAR_EXEC:
310*86f4a437SRobert Watson 				sc->avg_flags |= ALTERA_AVALON_FLAG_MMAP_EXEC;
311*86f4a437SRobert Watson 				break;
312*86f4a437SRobert Watson 
313*86f4a437SRobert Watson 			default:
314*86f4a437SRobert Watson 				device_printf(dev,
315*86f4a437SRobert Watson 				    "invalid %s character %c\n",
316*86f4a437SRobert Watson 				    ALTERA_AVALON_STR_MMAPIO, *cp);
317*86f4a437SRobert Watson 				return (ENXIO);
318*86f4a437SRobert Watson 			}
319*86f4a437SRobert Watson 		}
320*86f4a437SRobert Watson 	}
321*86f4a437SRobert Watson 	return (0);
322*86f4a437SRobert Watson }
323*86f4a437SRobert Watson 
324*86f4a437SRobert Watson static int
325*86f4a437SRobert Watson altera_avgen_nexus_attach(device_t dev)
326*86f4a437SRobert Watson {
327*86f4a437SRobert Watson 	struct altera_avgen_softc *sc;
328*86f4a437SRobert Watson 	const char *str_fileio, *str_mmapio;
329*86f4a437SRobert Watson 	const char *str_devname;
330*86f4a437SRobert Watson 	char devname[SPECNAMELEN + 1];
331*86f4a437SRobert Watson 	int devunit, error;
332*86f4a437SRobert Watson 
333*86f4a437SRobert Watson 	sc = device_get_softc(dev);
334*86f4a437SRobert Watson 	sc->avg_dev = dev;
335*86f4a437SRobert Watson 	sc->avg_unit = device_get_unit(dev);
336*86f4a437SRobert Watson 
337*86f4a437SRobert Watson 	/*
338*86f4a437SRobert Watson 	 * Query non-standard hints to find out what operations are permitted
339*86f4a437SRobert Watson 	 * on the device, and whether it is cached.
340*86f4a437SRobert Watson 	 */
341*86f4a437SRobert Watson 	str_fileio = NULL;
342*86f4a437SRobert Watson 	str_mmapio = NULL;
343*86f4a437SRobert Watson 	str_devname = NULL;
344*86f4a437SRobert Watson 	devunit = -1;
345*86f4a437SRobert Watson 	sc->avg_width = 1;
346*86f4a437SRobert Watson 	error = resource_int_value(device_get_name(dev), device_get_unit(dev),
347*86f4a437SRobert Watson 	    ALTERA_AVALON_STR_WIDTH, &sc->avg_width);
348*86f4a437SRobert Watson 	if (error != 0 && error != ENOENT) {
349*86f4a437SRobert Watson 		device_printf(dev, "invalid %s\n", ALTERA_AVALON_STR_WIDTH);
350*86f4a437SRobert Watson 		return (error);
351*86f4a437SRobert Watson 	}
352*86f4a437SRobert Watson 	(void)resource_string_value(device_get_name(dev),
353*86f4a437SRobert Watson 	    device_get_unit(dev), ALTERA_AVALON_STR_FILEIO, &str_fileio);
354*86f4a437SRobert Watson 	(void)resource_string_value(device_get_name(dev),
355*86f4a437SRobert Watson 	    device_get_unit(dev), ALTERA_AVALON_STR_MMAPIO, &str_mmapio);
356*86f4a437SRobert Watson 	(void)resource_string_value(device_get_name(dev),
357*86f4a437SRobert Watson 	    device_get_unit(dev), ALTERA_AVALON_STR_DEVNAME, &str_devname);
358*86f4a437SRobert Watson 	(void)resource_int_value(device_get_name(dev), device_get_unit(dev),
359*86f4a437SRobert Watson 	    ALTERA_AVALON_STR_DEVUNIT, &devunit);
360*86f4a437SRobert Watson 	error = altera_avgen_process_options(sc, str_fileio, str_mmapio,
361*86f4a437SRobert Watson 	    str_devname, devunit);
362*86f4a437SRobert Watson 	if (error)
363*86f4a437SRobert Watson 		return (error);
364*86f4a437SRobert Watson 
365*86f4a437SRobert Watson 	/* Select a device name. */
366*86f4a437SRobert Watson 	if (str_devname != NULL) {
367*86f4a437SRobert Watson 		if (devunit != -1)
368*86f4a437SRobert Watson 			(void)snprintf(devname, sizeof(devname), "%s%d",
369*86f4a437SRobert Watson 			    str_devname, devunit);
370*86f4a437SRobert Watson 		else
371*86f4a437SRobert Watson 			(void)snprintf(devname, sizeof(devname), "%s",
372*86f4a437SRobert Watson 			    str_devname);
373*86f4a437SRobert Watson 	} else
374*86f4a437SRobert Watson 		snprintf(devname, sizeof(devname), "%s%d", "avgen",
375*86f4a437SRobert Watson 		    sc->avg_unit);
376*86f4a437SRobert Watson 
377*86f4a437SRobert Watson 	/* Memory allocation and checking. */
378*86f4a437SRobert Watson 	sc->avg_rid = 0;
379*86f4a437SRobert Watson 	sc->avg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
380*86f4a437SRobert Watson 	    &sc->avg_rid, RF_ACTIVE);
381*86f4a437SRobert Watson 	if (sc->avg_res == NULL) {
382*86f4a437SRobert Watson 		device_printf(dev, "couldn't map memory\n");
383*86f4a437SRobert Watson 		return (ENXIO);
384*86f4a437SRobert Watson 	}
385*86f4a437SRobert Watson 	if (rman_get_size(sc->avg_res) >= PAGE_SIZE || str_mmapio != NULL) {
386*86f4a437SRobert Watson 		if (rman_get_size(sc->avg_res) % PAGE_SIZE != 0) {
387*86f4a437SRobert Watson 			device_printf(dev,
388*86f4a437SRobert Watson 			    "memory region not even multiple of page size\n");
389*86f4a437SRobert Watson 			error = ENXIO;
390*86f4a437SRobert Watson 			goto error;
391*86f4a437SRobert Watson 		}
392*86f4a437SRobert Watson 		if (rman_get_start(sc->avg_res) % PAGE_SIZE != 0) {
393*86f4a437SRobert Watson 			device_printf(dev, "memory region not page-aligned\n");
394*86f4a437SRobert Watson 			error = ENXIO;
395*86f4a437SRobert Watson 			goto error;
396*86f4a437SRobert Watson 		}
397*86f4a437SRobert Watson 	}
398*86f4a437SRobert Watson 
399*86f4a437SRobert Watson 	/* Device node allocation. */
400*86f4a437SRobert Watson 	if (str_devname == NULL) {
401*86f4a437SRobert Watson 		str_devname = "altera_avgen%d";
402*86f4a437SRobert Watson 		devunit = sc->avg_unit;
403*86f4a437SRobert Watson 	}
404*86f4a437SRobert Watson 	if (devunit != -1)
405*86f4a437SRobert Watson 		sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT,
406*86f4a437SRobert Watson 		    GID_WHEEL, S_IRUSR | S_IWUSR, str_devname, devunit);
407*86f4a437SRobert Watson 	else
408*86f4a437SRobert Watson 		sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT,
409*86f4a437SRobert Watson 		    GID_WHEEL, S_IRUSR | S_IWUSR, str_devname);
410*86f4a437SRobert Watson 	if (sc->avg_cdev == NULL) {
411*86f4a437SRobert Watson 		device_printf(sc->avg_dev, "%s: make_dev failed\n", __func__);
412*86f4a437SRobert Watson 		error = ENXIO;
413*86f4a437SRobert Watson 		goto error;
414*86f4a437SRobert Watson 	}
415*86f4a437SRobert Watson 	/* XXXRW: Slight race between make_dev(9) and here. */
416*86f4a437SRobert Watson 	sc->avg_cdev->si_drv1 = sc;
417*86f4a437SRobert Watson 	return (0);
418*86f4a437SRobert Watson 
419*86f4a437SRobert Watson error:
420*86f4a437SRobert Watson 	bus_release_resource(dev, SYS_RES_MEMORY, sc->avg_rid, sc->avg_res);
421*86f4a437SRobert Watson 	return (error);
422*86f4a437SRobert Watson }
423*86f4a437SRobert Watson 
424*86f4a437SRobert Watson static int
425*86f4a437SRobert Watson altera_avgen_nexus_detach(device_t dev)
426*86f4a437SRobert Watson {
427*86f4a437SRobert Watson 	struct altera_avgen_softc *sc;
428*86f4a437SRobert Watson 
429*86f4a437SRobert Watson 	sc = device_get_softc(dev);
430*86f4a437SRobert Watson 	destroy_dev(sc->avg_cdev);
431*86f4a437SRobert Watson 	bus_release_resource(dev, SYS_RES_MEMORY, sc->avg_rid, sc->avg_res);
432*86f4a437SRobert Watson 	return (0);
433*86f4a437SRobert Watson }
434*86f4a437SRobert Watson 
435*86f4a437SRobert Watson static device_method_t altera_avgen_nexus_methods[] = {
436*86f4a437SRobert Watson 	DEVMETHOD(device_probe,		altera_avgen_nexus_probe),
437*86f4a437SRobert Watson 	DEVMETHOD(device_attach,	altera_avgen_nexus_attach),
438*86f4a437SRobert Watson 	DEVMETHOD(device_detach,	altera_avgen_nexus_detach),
439*86f4a437SRobert Watson 	{ 0, 0 }
440*86f4a437SRobert Watson };
441*86f4a437SRobert Watson 
442*86f4a437SRobert Watson static driver_t altera_avgen_nexus_driver = {
443*86f4a437SRobert Watson 	"altera_avgen",
444*86f4a437SRobert Watson 	altera_avgen_nexus_methods,
445*86f4a437SRobert Watson 	sizeof(struct altera_avgen_softc),
446*86f4a437SRobert Watson };
447*86f4a437SRobert Watson 
448*86f4a437SRobert Watson static devclass_t altera_avgen_devclass;
449*86f4a437SRobert Watson 
450*86f4a437SRobert Watson DRIVER_MODULE(avgen, nexus, altera_avgen_nexus_driver, altera_avgen_devclass,
451*86f4a437SRobert Watson     0, 0);
452