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