xref: /freebsd/sys/dev/proto/proto_core.c (revision b78ee15e9f04ae15c3e1200df974473167524d17)
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_MEMORY != PROTO_RES_UNUSED &&
58     SYS_RES_IOPORT != PROTO_RES_UNUSED);
59 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
60     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
61     SYS_RES_IOPORT != PROTO_RES_PCICFG);
62 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA &&
63     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
64     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
65 
66 devclass_t proto_devclass;
67 char proto_driver_name[] = "proto";
68 
69 static d_open_t proto_open;
70 static d_close_t proto_close;
71 static d_read_t proto_read;
72 static d_write_t proto_write;
73 static d_ioctl_t proto_ioctl;
74 static d_mmap_t proto_mmap;
75 
76 struct cdevsw proto_devsw = {
77 	.d_version = D_VERSION,
78 	.d_flags = 0,
79 	.d_name = proto_driver_name,
80 	.d_open = proto_open,
81 	.d_close = proto_close,
82 	.d_read = proto_read,
83 	.d_write = proto_write,
84 	.d_ioctl = proto_ioctl,
85 	.d_mmap = proto_mmap,
86 };
87 
88 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
89 
90 int
91 proto_add_resource(struct proto_softc *sc, int type, int rid,
92     struct resource *res)
93 {
94 	struct proto_res *r;
95 
96 	if (type == PROTO_RES_UNUSED)
97 		return (EINVAL);
98 	if (sc->sc_rescnt == PROTO_RES_MAX)
99 		return (ENOSPC);
100 
101 	r = sc->sc_res + sc->sc_rescnt++;
102 	r->r_type = type;
103 	r->r_rid = rid;
104 	r->r_d.res = res;
105 	return (0);
106 }
107 
108 #ifdef notyet
109 static int
110 proto_intr(void *arg)
111 {
112 	struct proto_softc *sc = arg;
113 
114 	/* XXX TODO */
115 	return (FILTER_HANDLED);
116 }
117 #endif
118 
119 int
120 proto_attach(device_t dev)
121 {
122 	struct proto_softc *sc;
123 	struct proto_res *r;
124 	u_int res;
125 
126 	sc = device_get_softc(dev);
127 	sc->sc_dev = dev;
128 
129 	for (res = 0; res < sc->sc_rescnt; res++) {
130 		r = sc->sc_res + res;
131 		switch (r->r_type) {
132 		case SYS_RES_IRQ:
133 			/* XXX TODO */
134 			break;
135 		case SYS_RES_MEMORY:
136 		case SYS_RES_IOPORT:
137 			r->r_size = rman_get_size(r->r_d.res);
138 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
139 			    "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
140 			    (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
141 			r->r_u.cdev->si_drv1 = sc;
142 			r->r_u.cdev->si_drv2 = r;
143 			break;
144 		case PROTO_RES_PCICFG:
145 			r->r_size = 4096;
146 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
147 			    "proto/%s/pcicfg", device_get_desc(dev));
148 			r->r_u.cdev->si_drv1 = sc;
149 			r->r_u.cdev->si_drv2 = r;
150 			break;
151 		case PROTO_RES_BUSDMA:
152 			r->r_d.busdma = proto_busdma_attach(sc);
153 			r->r_size = 0;	/* no read(2) nor write(2) */
154 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
155 			    "proto/%s/busdma", device_get_desc(dev));
156 			r->r_u.cdev->si_drv1 = sc;
157 			r->r_u.cdev->si_drv2 = r;
158 			break;
159 		}
160 	}
161 	return (0);
162 }
163 
164 int
165 proto_detach(device_t dev)
166 {
167 	struct proto_softc *sc;
168 	struct proto_res *r;
169 	u_int res;
170 
171 	sc = device_get_softc(dev);
172 
173 	/* Don't detach if we have open device files. */
174 	for (res = 0; res < sc->sc_rescnt; res++) {
175 		r = sc->sc_res + res;
176 		if (r->r_opened)
177 			return (EBUSY);
178 	}
179 
180 	for (res = 0; res < sc->sc_rescnt; res++) {
181 		r = sc->sc_res + res;
182 		switch (r->r_type) {
183 		case SYS_RES_IRQ:
184 			/* XXX TODO */
185 			bus_release_resource(dev, r->r_type, r->r_rid,
186 			    r->r_d.res);
187 			break;
188 		case SYS_RES_MEMORY:
189 		case SYS_RES_IOPORT:
190 			bus_release_resource(dev, r->r_type, r->r_rid,
191 			    r->r_d.res);
192 			destroy_dev(r->r_u.cdev);
193 			break;
194 		case PROTO_RES_PCICFG:
195 			destroy_dev(r->r_u.cdev);
196 			break;
197 		case PROTO_RES_BUSDMA:
198 			proto_busdma_detach(sc, r->r_d.busdma);
199 			destroy_dev(r->r_u.cdev);
200 			break;
201 		}
202 		r->r_type = PROTO_RES_UNUSED;
203 	}
204 	sc->sc_rescnt = 0;
205 	return (0);
206 }
207 
208 /*
209  * Device functions
210  */
211 
212 static int
213 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
214 {
215 	struct proto_res *r;
216 
217 	r = cdev->si_drv2;
218 	if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
219 		return (EBUSY);
220 	return (0);
221 }
222 
223 static int
224 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
225 {
226 	struct proto_res *r;
227 	struct proto_softc *sc;
228 
229 	sc = cdev->si_drv1;
230 	r = cdev->si_drv2;
231 	if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
232 		return (ENXIO);
233 	if (r->r_type == PROTO_RES_BUSDMA)
234 		proto_busdma_cleanup(sc, r->r_d.busdma);
235 	return (0);
236 }
237 
238 static int
239 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
240 {
241 	union {
242 		uint8_t	x1[8];
243 		uint16_t x2[4];
244 		uint32_t x4[2];
245 		uint64_t x8[1];
246 	} buf;
247 	struct proto_softc *sc;
248 	struct proto_res *r;
249 	device_t dev;
250 	off_t ofs;
251 	u_long width;
252 	int error;
253 
254 	sc = cdev->si_drv1;
255 	dev = sc->sc_dev;
256 	r = cdev->si_drv2;
257 
258 	width = uio->uio_resid;
259 	if (width < 1 || width > 8 || bitcount16(width) > 1)
260 		return (EIO);
261 	ofs = uio->uio_offset;
262 	if (ofs + width > r->r_size)
263 		return (EIO);
264 
265 	switch (width) {
266 	case 1:
267 		buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
268 		    pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
269 		break;
270 	case 2:
271 		buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
272 		    pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
273 		break;
274 	case 4:
275 		buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
276 		    pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
277 		break;
278 #ifndef __i386__
279 	case 8:
280 		if (r->r_type == PROTO_RES_PCICFG)
281 			return (EINVAL);
282 		buf.x8[0] = bus_read_8(r->r_d.res, ofs);
283 		break;
284 #endif
285 	default:
286 		return (EIO);
287 	}
288 
289 	error = uiomove(&buf, width, uio);
290 	return (error);
291 }
292 
293 static int
294 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
295 {
296 	union {
297 		uint8_t	x1[8];
298 		uint16_t x2[4];
299 		uint32_t x4[2];
300 		uint64_t x8[1];
301 	} buf;
302 	struct proto_softc *sc;
303 	struct proto_res *r;
304 	device_t dev;
305 	off_t ofs;
306 	u_long width;
307 	int error;
308 
309 	sc = cdev->si_drv1;
310 	dev = sc->sc_dev;
311 	r = cdev->si_drv2;
312 
313 	width = uio->uio_resid;
314 	if (width < 1 || width > 8 || bitcount16(width) > 1)
315 		return (EIO);
316 	ofs = uio->uio_offset;
317 	if (ofs + width > r->r_size)
318 		return (EIO);
319 
320 	error = uiomove(&buf, width, uio);
321 	if (error)
322 		return (error);
323 
324 	switch (width) {
325 	case 1:
326 		if (r->r_type == PROTO_RES_PCICFG)
327 			pci_write_config(dev, ofs, buf.x1[0], 1);
328 		else
329 			bus_write_1(r->r_d.res, ofs, buf.x1[0]);
330 		break;
331 	case 2:
332 		if (r->r_type == PROTO_RES_PCICFG)
333 			pci_write_config(dev, ofs, buf.x2[0], 2);
334 		else
335 			bus_write_2(r->r_d.res, ofs, buf.x2[0]);
336 		break;
337 	case 4:
338 		if (r->r_type == PROTO_RES_PCICFG)
339 			pci_write_config(dev, ofs, buf.x4[0], 4);
340 		else
341 			bus_write_4(r->r_d.res, ofs, buf.x4[0]);
342 		break;
343 #ifndef __i386__
344 	case 8:
345 		if (r->r_type == PROTO_RES_PCICFG)
346 			return (EINVAL);
347 		bus_write_8(r->r_d.res, ofs, buf.x8[0]);
348 		break;
349 #endif
350 	default:
351 		return (EIO);
352 	}
353 
354 	return (0);
355 }
356 
357 static int
358 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
359     struct thread *td)
360 {
361 	struct proto_ioc_region *region;
362 	struct proto_ioc_busdma *busdma;
363 	struct proto_res *r;
364 	struct proto_softc *sc;
365 	int error;
366 
367 	sc = cdev->si_drv1;
368 	r = cdev->si_drv2;
369 
370 	error = 0;
371 	switch (cmd) {
372 	case PROTO_IOC_REGION:
373 		if (r->r_type == PROTO_RES_BUSDMA) {
374 			error = EINVAL;
375 			break;
376 		}
377 		region = (struct proto_ioc_region *)data;
378 		region->size = r->r_size;
379 		if (r->r_type == PROTO_RES_PCICFG)
380 			region->address = 0;
381 		else
382 			region->address = rman_get_start(r->r_d.res);
383 		break;
384 	case PROTO_IOC_BUSDMA:
385 		if (r->r_type != PROTO_RES_BUSDMA) {
386 			error = EINVAL;
387 			break;
388 		}
389 		busdma = (struct proto_ioc_busdma *)data;
390 		error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma);
391 		break;
392 	default:
393 		error = ENOIOCTL;
394 		break;
395 	}
396 	return (error);
397 }
398 
399 static int
400 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
401     int prot, vm_memattr_t *memattr)
402 {
403 	struct proto_res *r;
404 
405 	if (offset & PAGE_MASK)
406 		return (EINVAL);
407 	if (prot & PROT_EXEC)
408 		return (EACCES);
409 
410 	r = cdev->si_drv2;
411 
412 	switch (r->r_type) {
413 	case SYS_RES_MEMORY:
414 		if (offset >= r->r_size)
415 			return (EINVAL);
416 		*paddr = rman_get_start(r->r_d.res) + offset;
417 #ifndef __sparc64__
418 		*memattr = VM_MEMATTR_UNCACHEABLE;
419 #endif
420 		break;
421 	case PROTO_RES_BUSDMA:
422 		if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset))
423 			return (EINVAL);
424 		*paddr = offset;
425 		break;
426 	default:
427 		return (ENXIO);
428 	}
429 	return (0);
430 }
431