xref: /illumos-gate/usr/src/uts/i86pc/io/ioat/ioat_ioctl.c (revision 45d2468cd430f160914c353c714144054804373a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/errno.h>
30 #include <sys/types.h>
31 #include <sys/conf.h>
32 #include <sys/kmem.h>
33 #include <sys/ddi.h>
34 #include <sys/stat.h>
35 #include <sys/sunddi.h>
36 #include <sys/file.h>
37 #include <sys/open.h>
38 #include <sys/modctl.h>
39 #include <sys/ddi_impldefs.h>
40 #include <sys/sysmacros.h>
41 
42 #include <vm/hat.h>
43 #include <vm/as.h>
44 
45 #include <sys/ioat.h>
46 
47 
48 extern void *ioat_statep;
49 #define	ptob64(x)	(((uint64_t)(x)) << PAGESHIFT)
50 
51 static int ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode);
52 #ifdef	DEBUG
53 static int ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode);
54 static int ioat_ioctl_test(ioat_state_t *state, void *arg, int mode);
55 #endif
56 
57 /*
58  * ioat_ioctl()
59  */
60 /*ARGSUSED*/
61 int
62 ioat_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rval)
63 {
64 	ioat_state_t *state;
65 	int instance;
66 	int e;
67 
68 
69 	e = drv_priv(cred);
70 	if (e != 0) {
71 		return (EPERM);
72 	}
73 	instance = getminor(dev);
74 	if (instance == -1) {
75 		return (EBADF);
76 	}
77 	state = ddi_get_soft_state(ioat_statep, instance);
78 	if (state == NULL) {
79 		return (EBADF);
80 	}
81 
82 	switch (cmd) {
83 	case IOAT_IOCTL_READ_REG:
84 		e = ioat_ioctl_rdreg(state, (void *)arg, mode);
85 		break;
86 #ifdef	DEBUG
87 	case IOAT_IOCTL_WRITE_REG:
88 		e = ioat_ioctl_wrreg(state, (void *)arg, mode);
89 		break;
90 	case IOAT_IOCTL_TEST:
91 		e = ioat_ioctl_test(state, (void *)arg, mode);
92 		break;
93 #endif
94 
95 	default:
96 		e = ENXIO;
97 	}
98 
99 	return (e);
100 }
101 
102 
103 /*
104  * ioat_ioctl_rdreg()
105  */
106 static int
107 ioat_ioctl_rdreg(ioat_state_t *state, void *arg, int mode)
108 {
109 	ioat_ioctl_rdreg_t rdreg;
110 	int e;
111 
112 
113 	e = ddi_copyin(arg, &rdreg, sizeof (ioat_ioctl_rdreg_t), mode);
114 	if (e != 0) {
115 		return (EFAULT);
116 	}
117 
118 	/*
119 	 * read a device register, where size is read size in bits, addr is
120 	 * the offset into MMIO registers.
121 	 */
122 	switch (rdreg.size) {
123 	case 8:
124 		rdreg.data = (uint64_t)ddi_get8(state->is_reg_handle,
125 		    (uint8_t *)&state->is_genregs[rdreg.addr]);
126 		break;
127 	case 16:
128 		rdreg.data = (uint64_t)ddi_get16(state->is_reg_handle,
129 		    (uint16_t *)&state->is_genregs[rdreg.addr]);
130 		break;
131 	case 32:
132 		rdreg.data = (uint64_t)ddi_get32(state->is_reg_handle,
133 		    (uint32_t *)&state->is_genregs[rdreg.addr]);
134 		break;
135 	case 64:
136 		rdreg.data = (uint64_t)ddi_get64(state->is_reg_handle,
137 		    (uint64_t *)&state->is_genregs[rdreg.addr]);
138 		break;
139 	default:
140 		return (EFAULT);
141 	}
142 
143 	e = ddi_copyout(&rdreg, arg, sizeof (ioat_ioctl_rdreg_t), mode);
144 	if (e != 0) {
145 		return (EFAULT);
146 	}
147 
148 	return (0);
149 }
150 
151 
152 #ifdef	DEBUG
153 /*
154  * ioat_ioctl_wrreg()
155  */
156 static int
157 ioat_ioctl_wrreg(ioat_state_t *state, void *arg, int mode)
158 {
159 	ioat_ioctl_wrreg_t wrreg;
160 	int e;
161 
162 
163 	e = ddi_copyin(arg, &wrreg, sizeof (ioat_ioctl_wrreg_t), mode);
164 	if (e != 0) {
165 		return (EFAULT);
166 	}
167 
168 	/*
169 	 * write a device register, where size is write size in bits, addr is
170 	 * the offset into MMIO registers.
171 	 */
172 	switch (wrreg.size) {
173 	case 8:
174 		ddi_put8(state->is_reg_handle,
175 		    (uint8_t *)&state->is_genregs[wrreg.addr],
176 		    (uint8_t)wrreg.data);
177 		break;
178 	case 16:
179 		ddi_put16(state->is_reg_handle,
180 		    (uint16_t *)&state->is_genregs[wrreg.addr],
181 		    (uint16_t)wrreg.data);
182 		break;
183 	case 32:
184 		ddi_put32(state->is_reg_handle,
185 		    (uint32_t *)&state->is_genregs[wrreg.addr],
186 		    (uint32_t)wrreg.data);
187 		break;
188 	case 64:
189 		ddi_put64(state->is_reg_handle,
190 		    (uint64_t *)&state->is_genregs[wrreg.addr],
191 		    (uint64_t)wrreg.data);
192 		break;
193 	default:
194 		return (EFAULT);
195 	}
196 
197 	return (0);
198 }
199 
200 
201 /*
202  * ioat_ioctl_test()
203  */
204 /*ARGSUSED*/
205 static int
206 ioat_ioctl_test(ioat_state_t *state, void *arg, int mode)
207 {
208 	dcopy_handle_t channel;
209 	dcopy_cmd_t cmd;
210 	uint8_t *source;
211 	uint_t buf_size;
212 	uint_t poll_cnt;
213 	uint8_t *dest;
214 	uint8_t *buf;
215 	int flags;
216 	int i;
217 	int e;
218 
219 
220 	/* allocate 2 paged aligned 4k pages */
221 	buf_size = 0x1000;
222 	buf = kmem_zalloc((buf_size * 2) + 0x1000, KM_SLEEP);
223 	source = (uint8_t *)(((uintptr_t)buf + PAGEOFFSET) & PAGEMASK);
224 	dest = source + buf_size;
225 
226 	/* Init source buffer */
227 	for (i = 0; i < buf_size; i++) {
228 		source[i] = (uint8_t)(i & 0xFF);
229 	}
230 
231 	/* allocate a DMA channel */
232 	e = dcopy_alloc(DCOPY_SLEEP, &channel);
233 	if (e != DCOPY_SUCCESS) {
234 		cmn_err(CE_CONT, "dcopy_alloc() failed\n");
235 		goto testfail_alloc;
236 	}
237 
238 	/*
239 	 * post 32 DMA copy's from dest to dest.  These will complete in order
240 	 * so they won't stomp on each other. We don't care about the data
241 	 * right now which is why we go dest to dest.
242 	 */
243 	flags = DCOPY_SLEEP;
244 	for (i = 0; i < 32; i++) {
245 		/*
246 		 * if this is the second command, link the commands from here
247 		 * on out. We only want to keep track of the last command. We
248 		 * will poll on the last command completing (which infers that
249 		 * the other commands completed). If any of the previous
250 		 * commands fail, so will the last one. Linking the commands
251 		 * also allows us to only call free for the last command. free
252 		 * will free up the entire chain of commands.
253 		 */
254 		if (i == 1) {
255 			flags |= DCOPY_ALLOC_LINK;
256 		}
257 		e = dcopy_cmd_alloc(channel, flags, &cmd);
258 		if (e != DCOPY_SUCCESS) {
259 			cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n");
260 			goto testfail_alloc;
261 		}
262 
263 		ASSERT(cmd->dp_version == DCOPY_CMD_V0);
264 		cmd->dp_cmd = DCOPY_CMD_COPY;
265 		cmd->dp_flags = DCOPY_CMD_NOFLAGS;
266 
267 		/* do a bunch of dest to dest DMA's */
268 		cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat,
269 		    (caddr_t)source)) + ((uintptr_t)dest & PAGEOFFSET);
270 		cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat,
271 		    (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET);
272 		cmd->dp.copy.cc_size = PAGESIZE;
273 
274 		e = dcopy_cmd_post(cmd);
275 		if (e != DCOPY_SUCCESS) {
276 			cmn_err(CE_CONT, "dcopy_post() failed\n");
277 			goto testfail_post;
278 		}
279 	}
280 
281 	e = dcopy_cmd_alloc(channel, flags, &cmd);
282 	if (e != DCOPY_SUCCESS) {
283 		cmn_err(CE_CONT, "dcopy_cmd_alloc() failed\n");
284 		goto testfail_alloc;
285 	}
286 
287 	/* now queue up the DMA we are going to check status and data for  */
288 	cmd->dp_cmd = DCOPY_CMD_COPY;
289 	cmd->dp_flags = DCOPY_CMD_INTR;
290 	cmd->dp.copy.cc_source = ptob64(hat_getpfnum(kas.a_hat,
291 	    (caddr_t)source)) + ((uintptr_t)source & PAGEOFFSET);
292 	cmd->dp.copy.cc_dest = ptob64(hat_getpfnum(kas.a_hat,
293 	    (caddr_t)dest)) + ((uintptr_t)dest & PAGEOFFSET);
294 	cmd->dp.copy.cc_size = PAGESIZE;
295 	e = dcopy_cmd_post(cmd);
296 	if (e != DCOPY_SUCCESS) {
297 		cmn_err(CE_CONT, "dcopy_post() failed\n");
298 		goto testfail_post;
299 	}
300 
301 	/* check the status of the last command */
302 	poll_cnt = 0;
303 	flags = DCOPY_POLL_NOFLAGS;
304 	while ((e = dcopy_cmd_poll(cmd, flags)) == DCOPY_PENDING) {
305 		poll_cnt++;
306 		if (poll_cnt >= 16) {
307 			flags |= DCOPY_POLL_BLOCK;
308 		}
309 	}
310 	if (e != DCOPY_COMPLETED) {
311 		cmn_err(CE_CONT, "dcopy_poll() failed\n");
312 		goto testfail_poll;
313 	}
314 
315 	/* since the cmd's are linked we only need to pass in the last cmd */
316 	dcopy_cmd_free(&cmd);
317 	dcopy_free(&channel);
318 
319 	/* verify the data */
320 	for (i = 0; i < PAGESIZE; i++) {
321 		if (dest[i] != (uint8_t)(i & 0xFF)) {
322 			cmn_err(CE_CONT,
323 			    "dcopy_data_compare() failed, %p[%d]: %x, %x\n",
324 			    (void *)dest, i, dest[i], i & 0xFF);
325 			return (-1);
326 		}
327 	}
328 
329 	kmem_free(buf, (buf_size * 2) + 0x1000);
330 
331 	return (0);
332 
333 testfail_data_compare:
334 testfail_poll:
335 testfail_post:
336 	dcopy_cmd_free(&cmd);
337 	dcopy_free(&channel);
338 testfail_alloc:
339 	kmem_free(buf, (buf_size * 2) + 0x1000);
340 
341 	return (-1);
342 }
343 #endif
344