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