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