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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
31 #include <sys/sunddi.h>
32 #include <sys/ddi_impldefs.h>
34 #include <sys/dma_i8237A.h>
35 #include <sys/isadma.h>
36 #include <sys/nexusdebug.h>
38 /* Bitfield debugging definitions for this file */
39 #define ISADMA_MAP_DEBUG 0x1
40 #define ISADMA_REGACCESS_DEBUG 0x2
43 * The isadam nexus serves two functions. The first is to represent a
44 * a placeholder in the device tree for a shared dma controller register
45 * for the SuperIO floppy and parallel ports.
46 * The second function is to virtualize the shared dma controller register
47 * for those two drivers. Rather than creating new ddi routines to manage
48 * the shared register, we will use the ddi register mapping functions to
49 * do this. The two child devices will use ddi_regs_map_setup to map in
50 * their device registers. The isadma nexus will have an aliased entry in
51 * it's own registers property for the shared dma controller register. When
52 * the isadma detects the fact that it's children are trying to map the shared
53 * register, it will intercept this mapping and provide it's own register
54 * access routine to be used to access the register when the child devices
55 * use the ddi_{get,put} calls.
57 * Sigh, the 82C37 has a weird quirk (BUG?) where when DMA is active on the
58 * the bus, PIO's cannot happen. If they do, they generate bus faults and
59 * cause the system to panic. On PC's, the Intel processor has special
60 * req/grnt lines that prevent PIO's from occuring while DMA is in flight,
61 * unfortunately, hummingbird doesn't support this special req/grnt pair.
62 * I'm going to try and work around this by implementing a cv to stop PIO's
63 * from occuring while DMA is in flight. When each child wants to do DMA,
64 * they need to mask out all other channels using the allmask register.
65 * This nexus keys on this access and locks down the hardware using a cv.
66 * Once the driver's interrupt handler is called it needs to clear
67 * the allmask register. The nexus keys off of this an issues cv wakeups
71 * Function prototypes for busops routines:
73 static int isadma_map(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_map_req_t
*mp
,
74 off_t off
, off_t len
, caddr_t
*addrp
);
77 * function prototypes for dev ops routines:
79 static int isadma_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
);
80 static int isadma_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
);
83 * general function prototypes:
87 * bus ops and dev ops structures:
89 static struct bus_ops isadma_bus_ops
= {
106 0, /* (*bus_get_eventcookie)(); */
107 0, /* (*bus_add_eventcall)(); */
108 0, /* (*bus_remove_eventcall)(); */
109 0, /* (*bus_post_event)(); */
110 0, /* (*bus_intr_control)(); */
111 0, /* (*bus_config)(); */
112 0, /* (*bus_unconfig)(); */
113 0, /* (*bus_fm_init)(); */
114 0, /* (*bus_fm_fini)(); */
115 0, /* (*bus_fm_access_enter)(); */
116 0, /* (*bus_fm_access_exit)(); */
117 0, /* (*bus_power)(); */
118 i_ddi_intr_ops
/* (*bus_intr_op(); */
121 static struct dev_ops isadma_ops
= {
133 ddi_quiesce_not_needed
, /* quiesce */
137 * module definitions:
139 #include <sys/modctl.h>
141 static struct modldrv modldrv
= {
142 &mod_driverops
, /* Type of module. This one is a driver */
143 "isadma nexus driver", /* Name of module. */
144 &isadma_ops
, /* driver ops */
147 static struct modlinkage modlinkage
= {
148 MODREV_1
, (void *)&modldrv
, NULL
152 * driver global data:
154 static void *per_isadma_state
; /* per-isadma soft state pointer */
156 /* Global debug data */
157 uint64_t isadma_sleep_cnt
= 0;
158 uint64_t isadma_wakeup_cnt
= 0;
160 int64_t isadma_max_waiter
= 0;
161 int64_t isadma_min_waiter
= 0xffffll
;
162 uint64_t isadma_punt
= 0;
163 uint64_t isadma_setting_wdip
= 0;
164 uint64_t isadma_clearing_wdip
= 0;
173 * Initialize per-isadma soft state pointer.
175 e
= ddi_soft_state_init(&per_isadma_state
,
176 sizeof (isadma_devstate_t
), 1);
181 * Install the module.
183 e
= mod_install(&modlinkage
);
185 ddi_soft_state_fini(&per_isadma_state
);
197 e
= mod_remove(&modlinkage
);
202 * Free the soft state info.
204 ddi_soft_state_fini(&per_isadma_state
);
209 _info(struct modinfo
*modinfop
)
211 return (mod_info(&modlinkage
, modinfop
));
214 /* device driver entry points */
217 * attach entry point:
220 isadma_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
222 isadma_devstate_t
*isadmap
; /* per isadma state pointer */
224 int ret
= DDI_SUCCESS
;
227 debug_print_level
= 0;
233 * Allocate soft state for this instance.
235 instance
= ddi_get_instance(dip
);
236 if (ddi_soft_state_zalloc(per_isadma_state
, instance
)
241 isadmap
= ddi_get_soft_state(per_isadma_state
, instance
);
242 isadmap
->isadma_dip
= dip
;
244 /* Cache our register property */
245 if (ddi_getlongprop(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
246 "reg", (caddr_t
)&isadmap
->isadma_regp
,
247 &isadmap
->isadma_reglen
) != DDI_SUCCESS
) {
252 /* Initialize our mutex */
253 mutex_init(&isadmap
->isadma_access_lock
, NULL
, MUTEX_DRIVER
,
256 /* Initialize our condition variable */
257 cv_init(&isadmap
->isadma_access_cv
, NULL
, CV_DRIVER
, NULL
);
269 ddi_soft_state_free(per_isadma_state
, instance
);
276 * detach entry point:
279 isadma_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
281 int instance
= ddi_get_instance(dip
);
282 isadma_devstate_t
*isadmap
=
283 ddi_get_soft_state(per_isadma_state
, instance
);
287 cv_destroy(&isadmap
->isadma_access_cv
);
289 mutex_destroy(&isadmap
->isadma_access_lock
);
291 /* free the cached register property */
292 kmem_free(isadmap
->isadma_regp
, isadmap
->isadma_reglen
);
294 ddi_soft_state_free(per_isadma_state
, instance
);
295 return (DDI_SUCCESS
);
298 return (DDI_SUCCESS
);
300 return (DDI_FAILURE
);
306 isadma_check_waiters(isadma_devstate_t
*isadmap
)
308 if (isadmap
->isadma_want
> isadma_max_waiter
)
309 isadma_max_waiter
= isadmap
->isadma_want
;
311 if (isadmap
->isadma_want
< isadma_min_waiter
)
312 isadma_min_waiter
= isadmap
->isadma_want
;
317 isadma_dmawait(isadma_devstate_t
*isadmap
)
320 ASSERT(mutex_owned(&isadmap
->isadma_access_lock
));
322 /* Wait loop, if the locking dip is set, we wait. */
323 while (isadmap
->isadma_ldip
!= NULL
) {
325 isadmap
->isadma_want
++;
326 cv_wait(&isadmap
->isadma_access_cv
,
327 &isadmap
->isadma_access_lock
);
328 isadmap
->isadma_want
--;
334 isadma_wakeup(isadma_devstate_t
*isadmap
)
337 ASSERT(mutex_owned(&isadmap
->isadma_access_lock
));
340 * If somebody wants register access and the lock dip is not set
341 * signal the waiters.
343 if (isadmap
->isadma_want
> 0 && isadmap
->isadma_ldip
== NULL
) {
344 cv_signal(&isadmap
->isadma_access_cv
);
351 * Register access vectors
356 isadma_norep_get8(ddi_acc_impl_t
*handle
, uint8_t *host_addr
,
357 uint8_t *dev_addr
, size_t repcount
, uint_t flags
)
363 isadma_norep_get16(ddi_acc_impl_t
*handle
, uint16_t *host_addr
,
364 uint16_t *dev_addr
, size_t repcount
, uint_t flags
)
370 isadma_norep_get32(ddi_acc_impl_t
*handle
, uint32_t *host_addr
,
371 uint32_t *dev_addr
, size_t repcount
, uint_t flags
)
377 isadma_norep_get64(ddi_acc_impl_t
*handle
, uint64_t *host_addr
,
378 uint64_t *dev_addr
, size_t repcount
, uint_t flags
)
384 isadma_norep_put8(ddi_acc_impl_t
*handle
, uint8_t *host_addr
,
385 uint8_t *dev_addr
, size_t repcount
, uint_t flags
)
391 isadma_norep_put16(ddi_acc_impl_t
*handle
, uint16_t *host_addr
,
392 uint16_t *dev_addr
, size_t repcount
, uint_t flags
)
398 isadma_norep_put32(ddi_acc_impl_t
*handle
, uint32_t *host_addr
,
399 uint32_t *dev_addr
, size_t repcount
, uint_t flags
)
405 isadma_norep_put64(ddi_acc_impl_t
*handle
, uint64_t *host_addr
,
406 uint64_t *dev_addr
, size_t repcount
, uint_t flags
)
412 isadma_get8(ddi_acc_impl_t
*hdlp
, uint8_t *addr
)
414 ddi_acc_handle_t phdl
= hdlp
->ahi_common
.ah_platform_private
;
415 isadma_devstate_t
*isadmap
= hdlp
->ahi_common
.ah_bus_private
;
416 off_t offset
= (caddr_t
)addr
- hdlp
->ahi_common
.ah_addr
;
419 if (IN_CHILD_SPACE(offset
)) { /* Pass to parent */
423 return (ddi_get8(phdl
, addr
));
426 isadma_check_waiters(isadmap
);
428 mutex_enter(&isadmap
->isadma_access_lock
);
429 isadma_dmawait(isadmap
); /* wait until on-going dma completes */
431 /* No 8 bit access to 16 bit address or count registers */
432 if (IN_16BIT_SPACE(offset
))
435 /* No 8 bit access to first/last flip-flop registers */
436 if (IS_SEQREG(offset
))
439 ret
= ddi_get8(phdl
, addr
); /* Pass to parent */
441 isadma_wakeup(isadmap
);
442 mutex_exit(&isadmap
->isadma_access_lock
);
447 * Allow child devices to access this shared register set as if it were
448 * a real 16 bit register. The ISA bridge defines the access to this
449 * 16 bit dma controller & count register by programming an 8 byte register.
453 isadma_get16(ddi_acc_impl_t
*hdlp
, uint16_t *addr
)
455 ddi_acc_handle_t phdl
= hdlp
->ahi_common
.ah_platform_private
;
456 isadma_devstate_t
*isadmap
= hdlp
->ahi_common
.ah_bus_private
;
457 off_t offset
= (caddr_t
)addr
- hdlp
->ahi_common
.ah_addr
;
458 uint16_t ret
= 0xffff;
460 if (IN_CHILD_SPACE(offset
)) { /* Pass to parent */
464 return (ddi_get16(phdl
, addr
));
467 isadma_check_waiters(isadmap
);
469 mutex_enter(&isadmap
->isadma_access_lock
);
470 isadma_dmawait(isadmap
); /* wait until on-going dma completes */
472 /* Only Allow access to the 16 bit count and address registers */
473 if (!IN_16BIT_SPACE(offset
))
476 /* Set the sequencing register to the low byte */
477 ddi_put8(phdl
, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp
, offset
), 0);
479 /* Read the low byte, then high byte */
480 ret
= ddi_get8(phdl
, (uint8_t *)addr
);
481 ret
= (ddi_get8(phdl
, (uint8_t *)addr
) << 8) | ret
;
483 isadma_wakeup(isadmap
);
484 mutex_exit(&isadmap
->isadma_access_lock
);
490 isadma_noget32(ddi_acc_impl_t
*hdlp
, uint32_t *addr
)
497 isadma_noget64(ddi_acc_impl_t
*hdlp
, uint64_t *addr
)
503 * Here's where we do our locking magic. The dma all mask register is an 8
504 * bit register in the dma space, so we look for the access to the
505 * DMAC1_ALLMASK register. When somebody is masking out the dma channels
506 * we lock down the dma engine from further PIO accesses. When the driver
507 * calls back into this routine to clear the allmask register, we wakeup
508 * any blocked threads.
512 isadma_put8(ddi_acc_impl_t
*hdlp
, uint8_t *addr
, uint8_t value
)
514 ddi_acc_handle_t phdl
= hdlp
->ahi_common
.ah_platform_private
;
515 isadma_devstate_t
*isadmap
= hdlp
->ahi_common
.ah_bus_private
;
516 off_t offset
= (caddr_t
)addr
- hdlp
->ahi_common
.ah_addr
;
518 if (IN_CHILD_SPACE(offset
)) { /* Pass to parent */
522 ddi_put8(phdl
, addr
, value
);
526 isadma_check_waiters(isadmap
);
528 mutex_enter(&isadmap
->isadma_access_lock
);
530 if (isadmap
->isadma_ldip
== hdlp
->ahi_common
.ah_dip
) { /* owned lock? */
531 if (END_ISADMA(offset
, value
)) {
532 isadmap
->isadma_ldip
= NULL
; /* reset lock owner */
534 isadma_clearing_wdip
++;
537 } else { /* we don't own the lock */
538 /* wait until on-going dma completes */
539 isadma_dmawait(isadmap
);
541 if (BEGIN_ISADMA(offset
, value
)) {
542 isadmap
->isadma_ldip
= hdlp
->ahi_common
.ah_dip
;
544 isadma_setting_wdip
++;
549 /* No 8 bit access to 16 bit address or count registers */
550 if (IN_16BIT_SPACE(offset
))
553 /* No 8 bit access to first/last flip-flop registers */
554 if (IS_SEQREG(offset
))
557 ddi_put8(phdl
, addr
, value
); /* Pass to parent */
559 isadma_wakeup(isadmap
);
560 mutex_exit(&isadmap
->isadma_access_lock
);
564 * Allow child devices to access this shared register set as if it were
565 * a real 16 bit register. The ISA bridge defines the access to this
566 * 16 bit dma controller & count register by programming an 8 byte register.
570 isadma_put16(ddi_acc_impl_t
*hdlp
, uint16_t *addr
, uint16_t value
)
572 ddi_acc_handle_t phdl
= hdlp
->ahi_common
.ah_platform_private
;
573 isadma_devstate_t
*isadmap
= hdlp
->ahi_common
.ah_bus_private
;
574 off_t offset
= (caddr_t
)addr
- hdlp
->ahi_common
.ah_addr
;
576 if (IN_CHILD_SPACE(offset
)) { /* Pass to parent */
580 ddi_put16(phdl
, addr
, value
);
584 isadma_check_waiters(isadmap
);
586 mutex_enter(&isadmap
->isadma_access_lock
);
587 isadma_dmawait(isadmap
); /* wait until on-going dma completes */
589 /* Only Allow access to the 16 bit count and address registers */
590 if (!IN_16BIT_SPACE(offset
))
593 /* Set the sequencing register to the low byte */
594 ddi_put8(phdl
, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp
, offset
), 0);
596 /* Write the low byte, then the high byte */
597 ddi_put8(phdl
, (uint8_t *)addr
, value
& 0xff);
598 ddi_put8(phdl
, (uint8_t *)addr
, (value
>> 8) & 0xff);
600 isadma_wakeup(isadmap
);
601 mutex_exit(&isadmap
->isadma_access_lock
);
606 isadma_noput32(ddi_acc_impl_t
*hdlp
, uint32_t *addr
, uint32_t value
) {}
610 isadma_noput64(ddi_acc_impl_t
*hdlp
, uint64_t *addr
, uint64_t value
) {}
612 #define IS_SAME_REG(r1, r2) (((r1)->ebus_addr_hi == (r2)->ebus_addr_hi) && \
613 ((r1)->ebus_addr_low == (r2)->ebus_addr_low))
616 * The isadma_map routine determines if it's child is attempting to map a
617 * shared reg. If it is, it installs it's own vectors and bus private pointer
618 * and stacks those ops that were already defined.
621 isadma_map(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_map_req_t
*mp
,
622 off_t off
, off_t len
, caddr_t
*addrp
)
624 isadma_devstate_t
*isadmap
= ddi_get_soft_state(per_isadma_state
,
625 ddi_get_instance(dip
));
626 dev_info_t
*pdip
= (dev_info_t
*)DEVI(dip
)->devi_parent
;
627 ebus_regspec_t
*child_regp
, *regp
;
628 int32_t rnumber
= mp
->map_obj
.rnumber
;
634 * Get child regspec since the mapping struct may not have it yet
636 if (ddi_getlongprop(DDI_DEV_T_ANY
, rdip
, DDI_PROP_DONTPASS
,
637 "reg", (caddr_t
)®p
, ®len
) != DDI_SUCCESS
) {
638 return (DDI_FAILURE
);
641 child_regp
= regp
+ rnumber
;
643 DPRINTF(ISADMA_MAP_DEBUG
, ("isadma_map: child regp %p "
644 "parent regp %p Child reg array %p\n", (void *)child_regp
,
645 (void *)isadmap
->isadma_regp
, (void *)regp
));
647 /* Figure out if we're mapping or unmapping */
648 switch (mp
->map_op
) {
649 case DDI_MO_MAP_LOCKED
:
650 /* Call up device tree to establish mapping */
651 ret
= (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_map
)
652 (pdip
, rdip
, mp
, off
, len
, addrp
);
654 if ((ret
!= DDI_SUCCESS
) ||
655 !IS_SAME_REG(child_regp
, isadmap
->isadma_regp
))
658 /* Post-process the mapping request. */
659 hp
= kmem_alloc(sizeof (ddi_acc_impl_t
), KM_SLEEP
);
660 *hp
= *(ddi_acc_impl_t
*)mp
->map_handlep
;
661 impl_acc_hdl_get((ddi_acc_handle_t
)mp
->map_handlep
)->
662 ah_platform_private
= hp
;
663 hp
= (ddi_acc_impl_t
*)mp
->map_handlep
;
664 hp
->ahi_common
.ah_bus_private
= isadmap
;
665 hp
->ahi_get8
= isadma_get8
;
666 hp
->ahi_get16
= isadma_get16
;
667 hp
->ahi_get32
= isadma_noget32
;
668 hp
->ahi_get64
= isadma_noget64
;
669 hp
->ahi_put8
= isadma_put8
;
670 hp
->ahi_put16
= isadma_put16
;
671 hp
->ahi_put32
= isadma_noput32
;
672 hp
->ahi_put64
= isadma_noput64
;
673 hp
->ahi_rep_get8
= isadma_norep_get8
;
674 hp
->ahi_rep_get16
= isadma_norep_get16
;
675 hp
->ahi_rep_get32
= isadma_norep_get32
;
676 hp
->ahi_rep_get64
= isadma_norep_get64
;
677 hp
->ahi_rep_put8
= isadma_norep_put8
;
678 hp
->ahi_rep_put16
= isadma_norep_put16
;
679 hp
->ahi_rep_put32
= isadma_norep_put32
;
680 hp
->ahi_rep_put64
= isadma_norep_put64
;
684 if (IS_SAME_REG(child_regp
, isadmap
->isadma_regp
)) {
685 hp
= impl_acc_hdl_get(
686 (ddi_acc_handle_t
)mp
->map_handlep
)->
688 *(ddi_acc_impl_t
*)mp
->map_handlep
= *hp
;
689 kmem_free(hp
, sizeof (ddi_acc_impl_t
));
692 /* Call up tree to tear down mapping */
693 ret
= (DEVI(pdip
)->devi_ops
->devo_bus_ops
->bus_map
)
694 (pdip
, rdip
, mp
, off
, len
, addrp
);
702 kmem_free(regp
, reglen
);