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
ioat_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred,int * rval)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
ioat_ioctl_rdreg(ioat_state_t * state,void * arg,int mode)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
ioat_ioctl_wrreg(ioat_state_t * state,void * arg,int mode)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
ioat_ioctl_test(ioat_state_t * state,void * arg,int mode)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