xref: /freebsd/sys/dev/proto/proto_core.c (revision 89abdea8)
1 /*-
2  * Copyright (c) 2014 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/cons.h>
35 #include <sys/fcntl.h>
36 #include <sys/interrupt.h>
37 #include <sys/kdb.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/mman.h>
41 #include <sys/proc.h>
42 #include <sys/queue.h>
43 #include <sys/reboot.h>
44 #include <machine/bus.h>
45 #include <sys/rman.h>
46 #include <sys/uio.h>
47 #include <machine/resource.h>
48 #include <machine/stdarg.h>
49 
50 #include <dev/pci/pcivar.h>
51 
52 #include <dev/proto/proto.h>
53 #include <dev/proto/proto_dev.h>
54 #include <dev/proto/proto_busdma.h>
55 
56 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED &&
57     SYS_RES_DRQ != PROTO_RES_UNUSED &&
58     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
59     SYS_RES_IOPORT != PROTO_RES_UNUSED);
60 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
61     SYS_RES_DRQ != PROTO_RES_PCICFG &&
62     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
63     SYS_RES_IOPORT != PROTO_RES_PCICFG);
64 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA &&
65     SYS_RES_DRQ != PROTO_RES_BUSDMA &&
66     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
67     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
68 
69 devclass_t proto_devclass;
70 char proto_driver_name[] = "proto";
71 
72 static d_open_t proto_open;
73 static d_close_t proto_close;
74 static d_read_t proto_read;
75 static d_write_t proto_write;
76 static d_ioctl_t proto_ioctl;
77 static d_mmap_t proto_mmap;
78 
79 struct cdevsw proto_devsw = {
80 	.d_version = D_VERSION,
81 	.d_flags = 0,
82 	.d_name = proto_driver_name,
83 	.d_open = proto_open,
84 	.d_close = proto_close,
85 	.d_read = proto_read,
86 	.d_write = proto_write,
87 	.d_ioctl = proto_ioctl,
88 	.d_mmap = proto_mmap,
89 };
90 
91 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
92 
93 int
94 proto_add_resource(struct proto_softc *sc, int type, int rid,
95     struct resource *res)
96 {
97 	struct proto_res *r;
98 
99 	if (type == PROTO_RES_UNUSED)
100 		return (EINVAL);
101 	if (sc->sc_rescnt == PROTO_RES_MAX)
102 		return (ENOSPC);
103 
104 	r = sc->sc_res + sc->sc_rescnt++;
105 	r->r_type = type;
106 	r->r_rid = rid;
107 	r->r_d.res = res;
108 	return (0);
109 }
110 
111 #ifdef notyet
112 static int
113 proto_intr(void *arg)
114 {
115 	struct proto_softc *sc = arg;
116 
117 	/* XXX TODO */
118 	return (FILTER_HANDLED);
119 }
120 #endif
121 
122 int
123 proto_attach(device_t dev)
124 {
125 	struct proto_softc *sc;
126 	struct proto_res *r;
127 	u_int res;
128 
129 	sc = device_get_softc(dev);
130 	sc->sc_dev = dev;
131 
132 	for (res = 0; res < sc->sc_rescnt; res++) {
133 		r = sc->sc_res + res;
134 		switch (r->r_type) {
135 		case SYS_RES_IRQ:
136 			/* XXX TODO */
137 			break;
138 		case SYS_RES_DRQ:
139 			break;
140 		case SYS_RES_MEMORY:
141 		case SYS_RES_IOPORT:
142 			r->r_size = rman_get_size(r->r_d.res);
143 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
144 			    "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
145 			    (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
146 			r->r_u.cdev->si_drv1 = sc;
147 			r->r_u.cdev->si_drv2 = r;
148 			break;
149 		case PROTO_RES_PCICFG:
150 			r->r_size = 4096;
151 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
152 			    "proto/%s/pcicfg", device_get_desc(dev));
153 			r->r_u.cdev->si_drv1 = sc;
154 			r->r_u.cdev->si_drv2 = r;
155 			break;
156 		case PROTO_RES_BUSDMA:
157 			r->r_d.busdma = proto_busdma_attach(sc);
158 			r->r_size = 0;	/* no read(2) nor write(2) */
159 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
160 			    "proto/%s/busdma", device_get_desc(dev));
161 			r->r_u.cdev->si_drv1 = sc;
162 			r->r_u.cdev->si_drv2 = r;
163 			break;
164 		}
165 	}
166 	return (0);
167 }
168 
169 int
170 proto_detach(device_t dev)
171 {
172 	struct proto_softc *sc;
173 	struct proto_res *r;
174 	u_int res;
175 
176 	sc = device_get_softc(dev);
177 
178 	/* Don't detach if we have open device files. */
179 	for (res = 0; res < sc->sc_rescnt; res++) {
180 		r = sc->sc_res + res;
181 		if (r->r_opened)
182 			return (EBUSY);
183 	}
184 
185 	for (res = 0; res < sc->sc_rescnt; res++) {
186 		r = sc->sc_res + res;
187 		switch (r->r_type) {
188 		case SYS_RES_IRQ:
189 			/* XXX TODO */
190 			bus_release_resource(dev, r->r_type, r->r_rid,
191 			    r->r_d.res);
192 			break;
193 		case SYS_RES_DRQ:
194 			bus_release_resource(dev, r->r_type, r->r_rid,
195 			    r->r_d.res);
196 			break;
197 		case SYS_RES_MEMORY:
198 		case SYS_RES_IOPORT:
199 			bus_release_resource(dev, r->r_type, r->r_rid,
200 			    r->r_d.res);
201 			destroy_dev(r->r_u.cdev);
202 			break;
203 		case PROTO_RES_PCICFG:
204 			destroy_dev(r->r_u.cdev);
205 			break;
206 		case PROTO_RES_BUSDMA:
207 			proto_busdma_detach(sc, r->r_d.busdma);
208 			destroy_dev(r->r_u.cdev);
209 			break;
210 		}
211 		r->r_type = PROTO_RES_UNUSED;
212 	}
213 	sc->sc_rescnt = 0;
214 	return (0);
215 }
216 
217 /*
218  * Device functions
219  */
220 
221 static int
222 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
223 {
224 	struct proto_res *r;
225 
226 	r = cdev->si_drv2;
227 	if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
228 		return (EBUSY);
229 	return (0);
230 }
231 
232 static int
233 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
234 {
235 	struct proto_res *r;
236 	struct proto_softc *sc;
237 
238 	sc = cdev->si_drv1;
239 	r = cdev->si_drv2;
240 	if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
241 		return (ENXIO);
242 	if (r->r_type == PROTO_RES_BUSDMA)
243 		proto_busdma_cleanup(sc, r->r_d.busdma);
244 	return (0);
245 }
246 
247 static int
248 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
249 {
250 	union {
251 		uint8_t	x1[8];
252 		uint16_t x2[4];
253 		uint32_t x4[2];
254 		uint64_t x8[1];
255 	} buf;
256 	struct proto_softc *sc;
257 	struct proto_res *r;
258 	device_t dev;
259 	off_t ofs;
260 	u_long width;
261 	int error;
262 
263 	sc = cdev->si_drv1;
264 	dev = sc->sc_dev;
265 	r = cdev->si_drv2;
266 
267 	width = uio->uio_resid;
268 	if (width < 1 || width > 8 || bitcount16(width) > 1)
269 		return (EIO);
270 	ofs = uio->uio_offset;
271 	if (ofs + width > r->r_size)
272 		return (EIO);
273 
274 	switch (width) {
275 	case 1:
276 		buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
277 		    pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
278 		break;
279 	case 2:
280 		buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
281 		    pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
282 		break;
283 	case 4:
284 		buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
285 		    pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
286 		break;
287 #ifndef __i386__
288 	case 8:
289 		if (r->r_type == PROTO_RES_PCICFG)
290 			return (EINVAL);
291 		buf.x8[0] = bus_read_8(r->r_d.res, ofs);
292 		break;
293 #endif
294 	default:
295 		return (EIO);
296 	}
297 
298 	error = uiomove(&buf, width, uio);
299 	return (error);
300 }
301 
302 static int
303 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
304 {
305 	union {
306 		uint8_t	x1[8];
307 		uint16_t x2[4];
308 		uint32_t x4[2];
309 		uint64_t x8[1];
310 	} buf;
311 	struct proto_softc *sc;
312 	struct proto_res *r;
313 	device_t dev;
314 	off_t ofs;
315 	u_long width;
316 	int error;
317 
318 	sc = cdev->si_drv1;
319 	dev = sc->sc_dev;
320 	r = cdev->si_drv2;
321 
322 	width = uio->uio_resid;
323 	if (width < 1 || width > 8 || bitcount16(width) > 1)
324 		return (EIO);
325 	ofs = uio->uio_offset;
326 	if (ofs + width > r->r_size)
327 		return (EIO);
328 
329 	error = uiomove(&buf, width, uio);
330 	if (error)
331 		return (error);
332 
333 	switch (width) {
334 	case 1:
335 		if (r->r_type == PROTO_RES_PCICFG)
336 			pci_write_config(dev, ofs, buf.x1[0], 1);
337 		else
338 			bus_write_1(r->r_d.res, ofs, buf.x1[0]);
339 		break;
340 	case 2:
341 		if (r->r_type == PROTO_RES_PCICFG)
342 			pci_write_config(dev, ofs, buf.x2[0], 2);
343 		else
344 			bus_write_2(r->r_d.res, ofs, buf.x2[0]);
345 		break;
346 	case 4:
347 		if (r->r_type == PROTO_RES_PCICFG)
348 			pci_write_config(dev, ofs, buf.x4[0], 4);
349 		else
350 			bus_write_4(r->r_d.res, ofs, buf.x4[0]);
351 		break;
352 #ifndef __i386__
353 	case 8:
354 		if (r->r_type == PROTO_RES_PCICFG)
355 			return (EINVAL);
356 		bus_write_8(r->r_d.res, ofs, buf.x8[0]);
357 		break;
358 #endif
359 	default:
360 		return (EIO);
361 	}
362 
363 	return (0);
364 }
365 
366 static int
367 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
368     struct thread *td)
369 {
370 	struct proto_ioc_region *region;
371 	struct proto_ioc_busdma *busdma;
372 	struct proto_res *r;
373 	struct proto_softc *sc;
374 	int error;
375 
376 	sc = cdev->si_drv1;
377 	r = cdev->si_drv2;
378 
379 	error = 0;
380 	switch (cmd) {
381 	case PROTO_IOC_REGION:
382 		if (r->r_type == PROTO_RES_BUSDMA) {
383 			error = EINVAL;
384 			break;
385 		}
386 		region = (struct proto_ioc_region *)data;
387 		region->size = r->r_size;
388 		if (r->r_type == PROTO_RES_PCICFG)
389 			region->address = 0;
390 		else
391 			region->address = rman_get_start(r->r_d.res);
392 		break;
393 	case PROTO_IOC_BUSDMA:
394 		if (r->r_type != PROTO_RES_BUSDMA) {
395 			error = EINVAL;
396 			break;
397 		}
398 		busdma = (struct proto_ioc_busdma *)data;
399 		error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td);
400 		break;
401 	default:
402 		error = ENOIOCTL;
403 		break;
404 	}
405 	return (error);
406 }
407 
408 static int
409 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
410     int prot, vm_memattr_t *memattr)
411 {
412 	struct proto_res *r;
413 
414 	if (offset & PAGE_MASK)
415 		return (EINVAL);
416 	if (prot & PROT_EXEC)
417 		return (EACCES);
418 
419 	r = cdev->si_drv2;
420 
421 	switch (r->r_type) {
422 	case SYS_RES_MEMORY:
423 		if (offset >= r->r_size)
424 			return (EINVAL);
425 		*paddr = rman_get_start(r->r_d.res) + offset;
426 #ifndef __sparc64__
427 		*memattr = VM_MEMATTR_UNCACHEABLE;
428 #endif
429 		break;
430 	case PROTO_RES_BUSDMA:
431 		if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset))
432 			return (EINVAL);
433 		*paddr = offset;
434 		break;
435 	default:
436 		return (ENXIO);
437 	}
438 	return (0);
439 }
440