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