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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <sys/conf.h>
28 #include <sys/sunddi.h>
29 #include <sys/ddi_impldefs.h>
30 #include <sys/kmem.h>
31 #include <sys/dma_i8237A.h>
32 #include <sys/isadma.h>
33 #include <sys/nexusdebug.h>
34
35 /* Bitfield debugging definitions for this file */
36 #define ISADMA_MAP_DEBUG 0x1
37 #define ISADMA_REGACCESS_DEBUG 0x2
38
39 /*
40 * The isadam nexus serves two functions. The first is to represent a
41 * a placeholder in the device tree for a shared dma controller register
42 * for the SuperIO floppy and parallel ports.
43 * The second function is to virtualize the shared dma controller register
44 * for those two drivers. Rather than creating new ddi routines to manage
45 * the shared register, we will use the ddi register mapping functions to
46 * do this. The two child devices will use ddi_regs_map_setup to map in
47 * their device registers. The isadma nexus will have an aliased entry in
48 * it's own registers property for the shared dma controller register. When
49 * the isadma detects the fact that it's children are trying to map the shared
50 * register, it will intercept this mapping and provide it's own register
51 * access routine to be used to access the register when the child devices
52 * use the ddi_{get,put} calls.
53 *
54 * Sigh, the 82C37 has a weird quirk (BUG?) where when DMA is active on the
55 * the bus, PIO's cannot happen. If they do, they generate bus faults and
56 * cause the system to panic. On PC's, the Intel processor has special
57 * req/grnt lines that prevent PIO's from occuring while DMA is in flight,
58 * unfortunately, hummingbird doesn't support this special req/grnt pair.
59 * I'm going to try and work around this by implementing a cv to stop PIO's
60 * from occuring while DMA is in flight. When each child wants to do DMA,
61 * they need to mask out all other channels using the allmask register.
62 * This nexus keys on this access and locks down the hardware using a cv.
63 * Once the driver's interrupt handler is called it needs to clear
64 * the allmask register. The nexus keys off of this an issues cv wakeups
65 * if necessary.
66 */
67 /*
68 * Function prototypes for busops routines:
69 */
70 static int isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
71 off_t off, off_t len, caddr_t *addrp);
72
73 /*
74 * function prototypes for dev ops routines:
75 */
76 static int isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
77 static int isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
78
79 /*
80 * general function prototypes:
81 */
82
83 /*
84 * bus ops and dev ops structures:
85 */
86 static struct bus_ops isadma_bus_ops = {
87 BUSO_REV,
88 isadma_map,
89 NULL,
90 NULL,
91 NULL,
92 i_ddi_map_fault,
93 ddi_dma_map,
94 ddi_dma_allochdl,
95 ddi_dma_freehdl,
96 ddi_dma_bindhdl,
97 ddi_dma_unbindhdl,
98 ddi_dma_flush,
99 ddi_dma_win,
100 ddi_dma_mctl,
101 ddi_ctlops,
102 ddi_bus_prop_op,
103 0, /* (*bus_get_eventcookie)(); */
104 0, /* (*bus_add_eventcall)(); */
105 0, /* (*bus_remove_eventcall)(); */
106 0, /* (*bus_post_event)(); */
107 0, /* (*bus_intr_control)(); */
108 0, /* (*bus_config)(); */
109 0, /* (*bus_unconfig)(); */
110 0, /* (*bus_fm_init)(); */
111 0, /* (*bus_fm_fini)(); */
112 0, /* (*bus_fm_access_enter)(); */
113 0, /* (*bus_fm_access_exit)(); */
114 0, /* (*bus_power)(); */
115 i_ddi_intr_ops /* (*bus_intr_op(); */
116 };
117
118 static struct dev_ops isadma_ops = {
119 DEVO_REV,
120 0,
121 ddi_no_info,
122 nulldev,
123 0,
124 isadma_attach,
125 isadma_detach,
126 nodev,
127 (struct cb_ops *)0,
128 &isadma_bus_ops,
129 NULL,
130 ddi_quiesce_not_needed, /* quiesce */
131 };
132
133 /*
134 * module definitions:
135 */
136 #include <sys/modctl.h>
137
138 static struct modldrv modldrv = {
139 &mod_driverops, /* Type of module. This one is a driver */
140 "isadma nexus driver", /* Name of module. */
141 &isadma_ops, /* driver ops */
142 };
143
144 static struct modlinkage modlinkage = {
145 MODREV_1, (void *)&modldrv, NULL
146 };
147
148 /*
149 * driver global data:
150 */
151 static void *per_isadma_state; /* per-isadma soft state pointer */
152
153 /* Global debug data */
154 uint64_t isadma_sleep_cnt = 0;
155 uint64_t isadma_wakeup_cnt = 0;
156 #ifdef DEBUG
157 int64_t isadma_max_waiter = 0;
158 int64_t isadma_min_waiter = 0xffffll;
159 uint64_t isadma_punt = 0;
160 uint64_t isadma_setting_wdip = 0;
161 uint64_t isadma_clearing_wdip = 0;
162 #endif
163
164 int
_init(void)165 _init(void)
166 {
167 int e;
168
169 /*
170 * Initialize per-isadma soft state pointer.
171 */
172 e = ddi_soft_state_init(&per_isadma_state,
173 sizeof (isadma_devstate_t), 1);
174 if (e != 0)
175 return (e);
176
177 /*
178 * Install the module.
179 */
180 e = mod_install(&modlinkage);
181 if (e != 0)
182 ddi_soft_state_fini(&per_isadma_state);
183 return (e);
184 }
185
186 int
_fini(void)187 _fini(void)
188 {
189 int e;
190
191 /*
192 * Remove the module.
193 */
194 e = mod_remove(&modlinkage);
195 if (e != 0)
196 return (e);
197
198 /*
199 * Free the soft state info.
200 */
201 ddi_soft_state_fini(&per_isadma_state);
202 return (e);
203 }
204
205 int
_info(struct modinfo * modinfop)206 _info(struct modinfo *modinfop)
207 {
208 return (mod_info(&modlinkage, modinfop));
209 }
210
211 /* device driver entry points */
212
213 /*
214 * attach entry point:
215 */
216 static int
isadma_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)217 isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
218 {
219 isadma_devstate_t *isadmap; /* per isadma state pointer */
220 int32_t instance;
221 int ret = DDI_SUCCESS;
222
223 #ifdef DEBUG
224 debug_print_level = 0;
225 debug_info = 1;
226 #endif
227 switch (cmd) {
228 case DDI_ATTACH: {
229 /*
230 * Allocate soft state for this instance.
231 */
232 instance = ddi_get_instance(dip);
233 if (ddi_soft_state_zalloc(per_isadma_state, instance)
234 != DDI_SUCCESS) {
235 ret = DDI_FAILURE;
236 goto exit;
237 }
238 isadmap = ddi_get_soft_state(per_isadma_state, instance);
239 isadmap->isadma_dip = dip;
240
241 /* Cache our register property */
242 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
243 "reg", (caddr_t)&isadmap->isadma_regp,
244 &isadmap->isadma_reglen) != DDI_SUCCESS) {
245 ret = DDI_FAILURE;
246 goto fail_get_prop;
247 }
248
249 /* Initialize our mutex */
250 mutex_init(&isadmap->isadma_access_lock, NULL, MUTEX_DRIVER,
251 NULL);
252
253 /* Initialize our condition variable */
254 cv_init(&isadmap->isadma_access_cv, NULL, CV_DRIVER, NULL);
255
256 ddi_report_dev(dip);
257 goto exit;
258
259 }
260 case DDI_RESUME:
261 default:
262 goto exit;
263 }
264
265 fail_get_prop:
266 ddi_soft_state_free(per_isadma_state, instance);
267
268 exit:
269 return (ret);
270 }
271
272 /*
273 * detach entry point:
274 */
275 static int
isadma_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)276 isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
277 {
278 int instance = ddi_get_instance(dip);
279 isadma_devstate_t *isadmap =
280 ddi_get_soft_state(per_isadma_state, instance);
281
282 switch (cmd) {
283 case DDI_DETACH:
284 cv_destroy(&isadmap->isadma_access_cv);
285
286 mutex_destroy(&isadmap->isadma_access_lock);
287
288 /* free the cached register property */
289 kmem_free(isadmap->isadma_regp, isadmap->isadma_reglen);
290
291 ddi_soft_state_free(per_isadma_state, instance);
292 return (DDI_SUCCESS);
293
294 case DDI_SUSPEND:
295 return (DDI_SUCCESS);
296 }
297 return (DDI_FAILURE);
298 }
299
300
301 #ifdef DEBUG
302 static void
isadma_check_waiters(isadma_devstate_t * isadmap)303 isadma_check_waiters(isadma_devstate_t *isadmap)
304 {
305 if (isadmap->isadma_want > isadma_max_waiter)
306 isadma_max_waiter = isadmap->isadma_want;
307
308 if (isadmap->isadma_want < isadma_min_waiter)
309 isadma_min_waiter = isadmap->isadma_want;
310 }
311 #endif
312
313 static void
isadma_dmawait(isadma_devstate_t * isadmap)314 isadma_dmawait(isadma_devstate_t *isadmap)
315 {
316
317 ASSERT(mutex_owned(&isadmap->isadma_access_lock));
318
319 /* Wait loop, if the locking dip is set, we wait. */
320 while (isadmap->isadma_ldip != NULL) {
321
322 isadmap->isadma_want++;
323 cv_wait(&isadmap->isadma_access_cv,
324 &isadmap->isadma_access_lock);
325 isadmap->isadma_want--;
326 isadma_sleep_cnt++;
327 }
328 }
329
330 static void
isadma_wakeup(isadma_devstate_t * isadmap)331 isadma_wakeup(isadma_devstate_t *isadmap)
332 {
333
334 ASSERT(mutex_owned(&isadmap->isadma_access_lock));
335
336 /*
337 * If somebody wants register access and the lock dip is not set
338 * signal the waiters.
339 */
340 if (isadmap->isadma_want > 0 && isadmap->isadma_ldip == NULL) {
341 cv_signal(&isadmap->isadma_access_cv);
342 isadma_wakeup_cnt++;
343 }
344
345 }
346
347 /*
348 * Register access vectors
349 */
350
351 /*ARGSUSED*/
352 void
isadma_norep_get8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)353 isadma_norep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr,
354 uint8_t *dev_addr, size_t repcount, uint_t flags)
355 {
356 }
357
358 /*ARGSUSED*/
359 void
isadma_norep_get16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)360 isadma_norep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr,
361 uint16_t *dev_addr, size_t repcount, uint_t flags)
362 {
363 }
364
365 /*ARGSUSED*/
366 void
isadma_norep_get32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)367 isadma_norep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr,
368 uint32_t *dev_addr, size_t repcount, uint_t flags)
369 {
370 }
371
372 /*ARGSUSED*/
373 void
isadma_norep_get64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)374 isadma_norep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr,
375 uint64_t *dev_addr, size_t repcount, uint_t flags)
376 {
377 }
378
379 /*ARGSUSED*/
380 void
isadma_norep_put8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)381 isadma_norep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr,
382 uint8_t *dev_addr, size_t repcount, uint_t flags)
383 {
384 }
385
386 /*ARGSUSED*/
387 void
isadma_norep_put16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)388 isadma_norep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr,
389 uint16_t *dev_addr, size_t repcount, uint_t flags)
390 {
391 }
392
393 /*ARGSUSED*/
394 void
isadma_norep_put32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)395 isadma_norep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr,
396 uint32_t *dev_addr, size_t repcount, uint_t flags)
397 {
398 }
399
400 /*ARGSUSED*/
401 void
isadma_norep_put64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)402 isadma_norep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr,
403 uint64_t *dev_addr, size_t repcount, uint_t flags)
404 {
405 }
406
407 /*ARGSUSED*/
408 uint8_t
isadma_get8(ddi_acc_impl_t * hdlp,uint8_t * addr)409 isadma_get8(ddi_acc_impl_t *hdlp, uint8_t *addr)
410 {
411 ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
412 isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
413 off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
414 uint8_t ret = 0xff;
415
416 if (IN_CHILD_SPACE(offset)) { /* Pass to parent */
417 #ifdef DEBUG
418 isadma_punt++;
419 #endif
420 return (ddi_get8(phdl, addr));
421 }
422 #ifdef DEBUG
423 isadma_check_waiters(isadmap);
424 #endif
425 mutex_enter(&isadmap->isadma_access_lock);
426 isadma_dmawait(isadmap); /* wait until on-going dma completes */
427
428 /* No 8 bit access to 16 bit address or count registers */
429 if (IN_16BIT_SPACE(offset))
430 goto exit;
431
432 /* No 8 bit access to first/last flip-flop registers */
433 if (IS_SEQREG(offset))
434 goto exit;
435
436 ret = ddi_get8(phdl, addr); /* Pass to parent */
437 exit:
438 isadma_wakeup(isadmap);
439 mutex_exit(&isadmap->isadma_access_lock);
440 return (ret);
441 }
442
443 /*
444 * Allow child devices to access this shared register set as if it were
445 * a real 16 bit register. The ISA bridge defines the access to this
446 * 16 bit dma controller & count register by programming an 8 byte register.
447 */
448 /*ARGSUSED*/
449 uint16_t
isadma_get16(ddi_acc_impl_t * hdlp,uint16_t * addr)450 isadma_get16(ddi_acc_impl_t *hdlp, uint16_t *addr)
451 {
452 ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
453 isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
454 off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
455 uint16_t ret = 0xffff;
456
457 if (IN_CHILD_SPACE(offset)) { /* Pass to parent */
458 #ifdef DEBUG
459 isadma_punt++;
460 #endif
461 return (ddi_get16(phdl, addr));
462 }
463 #ifdef DEBUG
464 isadma_check_waiters(isadmap);
465 #endif
466 mutex_enter(&isadmap->isadma_access_lock);
467 isadma_dmawait(isadmap); /* wait until on-going dma completes */
468
469 /* Only Allow access to the 16 bit count and address registers */
470 if (!IN_16BIT_SPACE(offset))
471 goto exit;
472
473 /* Set the sequencing register to the low byte */
474 ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0);
475
476 /* Read the low byte, then high byte */
477 ret = ddi_get8(phdl, (uint8_t *)addr);
478 ret = (ddi_get8(phdl, (uint8_t *)addr) << 8) | ret;
479 exit:
480 isadma_wakeup(isadmap);
481 mutex_exit(&isadmap->isadma_access_lock);
482 return (ret);
483 }
484
485 /*ARGSUSED*/
486 uint32_t
isadma_noget32(ddi_acc_impl_t * hdlp,uint32_t * addr)487 isadma_noget32(ddi_acc_impl_t *hdlp, uint32_t *addr)
488 {
489 return (UINT32_MAX);
490 }
491
492 /*ARGSUSED*/
493 uint64_t
isadma_noget64(ddi_acc_impl_t * hdlp,uint64_t * addr)494 isadma_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr)
495 {
496 return (UINT64_MAX);
497 }
498
499 /*
500 * Here's where we do our locking magic. The dma all mask register is an 8
501 * bit register in the dma space, so we look for the access to the
502 * DMAC1_ALLMASK register. When somebody is masking out the dma channels
503 * we lock down the dma engine from further PIO accesses. When the driver
504 * calls back into this routine to clear the allmask register, we wakeup
505 * any blocked threads.
506 */
507 /*ARGSUSED*/
508 void
isadma_put8(ddi_acc_impl_t * hdlp,uint8_t * addr,uint8_t value)509 isadma_put8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
510 {
511 ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
512 isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
513 off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
514
515 if (IN_CHILD_SPACE(offset)) { /* Pass to parent */
516 #ifdef DEBUG
517 isadma_punt++;
518 #endif
519 ddi_put8(phdl, addr, value);
520 return;
521 }
522 #ifdef DEBUG
523 isadma_check_waiters(isadmap);
524 #endif
525 mutex_enter(&isadmap->isadma_access_lock);
526
527 if (isadmap->isadma_ldip == hdlp->ahi_common.ah_dip) { /* owned lock? */
528 if (END_ISADMA(offset, value)) {
529 isadmap->isadma_ldip = NULL; /* reset lock owner */
530 #ifdef DEBUG
531 isadma_clearing_wdip++;
532 #endif
533 }
534 } else { /* we don't own the lock */
535 /* wait until on-going dma completes */
536 isadma_dmawait(isadmap);
537
538 if (BEGIN_ISADMA(offset, value)) {
539 isadmap->isadma_ldip = hdlp->ahi_common.ah_dip;
540 #ifdef DEBUG
541 isadma_setting_wdip++;
542 #endif
543 }
544 }
545
546 /* No 8 bit access to 16 bit address or count registers */
547 if (IN_16BIT_SPACE(offset))
548 goto exit;
549
550 /* No 8 bit access to first/last flip-flop registers */
551 if (IS_SEQREG(offset))
552 goto exit;
553
554 ddi_put8(phdl, addr, value); /* Pass to parent */
555 exit:
556 isadma_wakeup(isadmap);
557 mutex_exit(&isadmap->isadma_access_lock);
558 }
559
560 /*
561 * Allow child devices to access this shared register set as if it were
562 * a real 16 bit register. The ISA bridge defines the access to this
563 * 16 bit dma controller & count register by programming an 8 byte register.
564 */
565 /*ARGSUSED*/
566 void
isadma_put16(ddi_acc_impl_t * hdlp,uint16_t * addr,uint16_t value)567 isadma_put16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
568 {
569 ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
570 isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
571 off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
572
573 if (IN_CHILD_SPACE(offset)) { /* Pass to parent */
574 #ifdef DEBUG
575 isadma_punt++;
576 #endif
577 ddi_put16(phdl, addr, value);
578 return;
579 }
580 #ifdef DEBUG
581 isadma_check_waiters(isadmap);
582 #endif
583 mutex_enter(&isadmap->isadma_access_lock);
584 isadma_dmawait(isadmap); /* wait until on-going dma completes */
585
586 /* Only Allow access to the 16 bit count and address registers */
587 if (!IN_16BIT_SPACE(offset))
588 goto exit;
589
590 /* Set the sequencing register to the low byte */
591 ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0);
592
593 /* Write the low byte, then the high byte */
594 ddi_put8(phdl, (uint8_t *)addr, value & 0xff);
595 ddi_put8(phdl, (uint8_t *)addr, (value >> 8) & 0xff);
596 exit:
597 isadma_wakeup(isadmap);
598 mutex_exit(&isadmap->isadma_access_lock);
599 }
600
601 /*ARGSUSED*/
602 void
isadma_noput32(ddi_acc_impl_t * hdlp,uint32_t * addr,uint32_t value)603 isadma_noput32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) {}
604
605 /*ARGSUSED*/
606 void
isadma_noput64(ddi_acc_impl_t * hdlp,uint64_t * addr,uint64_t value)607 isadma_noput64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) {}
608
609 #define IS_SAME_REG(r1, r2) (((r1)->ebus_addr_hi == (r2)->ebus_addr_hi) && \
610 ((r1)->ebus_addr_low == (r2)->ebus_addr_low))
611
612 /*
613 * The isadma_map routine determines if it's child is attempting to map a
614 * shared reg. If it is, it installs it's own vectors and bus private pointer
615 * and stacks those ops that were already defined.
616 */
617 static int
isadma_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)618 isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
619 off_t off, off_t len, caddr_t *addrp)
620 {
621 isadma_devstate_t *isadmap = ddi_get_soft_state(per_isadma_state,
622 ddi_get_instance(dip));
623 dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
624 ebus_regspec_t *child_regp, *regp;
625 int32_t rnumber = mp->map_obj.rnumber;
626 int32_t reglen;
627 int ret;
628 ddi_acc_impl_t *hp;
629
630 /*
631 * Get child regspec since the mapping struct may not have it yet
632 */
633 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
634 "reg", (caddr_t)®p, ®len) != DDI_SUCCESS) {
635 return (DDI_FAILURE);
636 }
637
638 child_regp = regp + rnumber;
639
640 DPRINTF(ISADMA_MAP_DEBUG, ("isadma_map: child regp %p "
641 "parent regp %p Child reg array %p\n", (void *)child_regp,
642 (void *)isadmap->isadma_regp, (void *)regp));
643
644 /* Figure out if we're mapping or unmapping */
645 switch (mp->map_op) {
646 case DDI_MO_MAP_LOCKED:
647 /* Call up device tree to establish mapping */
648 ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)
649 (pdip, rdip, mp, off, len, addrp);
650
651 if ((ret != DDI_SUCCESS) ||
652 !IS_SAME_REG(child_regp, isadmap->isadma_regp))
653 break;
654
655 /* Post-process the mapping request. */
656 hp = kmem_alloc(sizeof (ddi_acc_impl_t), KM_SLEEP);
657 *hp = *(ddi_acc_impl_t *)mp->map_handlep;
658 impl_acc_hdl_get((ddi_acc_handle_t)mp->map_handlep)->
659 ah_platform_private = hp;
660 hp = (ddi_acc_impl_t *)mp->map_handlep;
661 hp->ahi_common.ah_bus_private = isadmap;
662 hp->ahi_get8 = isadma_get8;
663 hp->ahi_get16 = isadma_get16;
664 hp->ahi_get32 = isadma_noget32;
665 hp->ahi_get64 = isadma_noget64;
666 hp->ahi_put8 = isadma_put8;
667 hp->ahi_put16 = isadma_put16;
668 hp->ahi_put32 = isadma_noput32;
669 hp->ahi_put64 = isadma_noput64;
670 hp->ahi_rep_get8 = isadma_norep_get8;
671 hp->ahi_rep_get16 = isadma_norep_get16;
672 hp->ahi_rep_get32 = isadma_norep_get32;
673 hp->ahi_rep_get64 = isadma_norep_get64;
674 hp->ahi_rep_put8 = isadma_norep_put8;
675 hp->ahi_rep_put16 = isadma_norep_put16;
676 hp->ahi_rep_put32 = isadma_norep_put32;
677 hp->ahi_rep_put64 = isadma_norep_put64;
678 break;
679
680 case DDI_MO_UNMAP:
681 if (IS_SAME_REG(child_regp, isadmap->isadma_regp)) {
682 hp = impl_acc_hdl_get(
683 (ddi_acc_handle_t)mp->map_handlep)->
684 ah_platform_private;
685 *(ddi_acc_impl_t *)mp->map_handlep = *hp;
686 kmem_free(hp, sizeof (ddi_acc_impl_t));
687 }
688
689 /* Call up tree to tear down mapping */
690 ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)
691 (pdip, rdip, mp, off, len, addrp);
692 break;
693
694 default:
695 ret = DDI_FAILURE;
696 break;
697 }
698
699 kmem_free(regp, reglen);
700 return (ret);
701 }
702