/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
*/
#include <sys/ddi_impldefs.h>
#include <sys/dma_i8237A.h>
#include <sys/nexusdebug.h>
/* Bitfield debugging definitions for this file */
/*
* The isadam nexus serves two functions. The first is to represent a
* a placeholder in the device tree for a shared dma controller register
* for the SuperIO floppy and parallel ports.
* The second function is to virtualize the shared dma controller register
* for those two drivers. Rather than creating new ddi routines to manage
* the shared register, we will use the ddi register mapping functions to
* do this. The two child devices will use ddi_regs_map_setup to map in
* their device registers. The isadma nexus will have an aliased entry in
* it's own registers property for the shared dma controller register. When
* the isadma detects the fact that it's children are trying to map the shared
* register, it will intercept this mapping and provide it's own register
* access routine to be used to access the register when the child devices
* use the ddi_{get,put} calls.
*
* Sigh, the 82C37 has a weird quirk (BUG?) where when DMA is active on the
* the bus, PIO's cannot happen. If they do, they generate bus faults and
* cause the system to panic. On PC's, the Intel processor has special
* I'm going to try and work around this by implementing a cv to stop PIO's
* from occuring while DMA is in flight. When each child wants to do DMA,
* they need to mask out all other channels using the allmask register.
* This nexus keys on this access and locks down the hardware using a cv.
* Once the driver's interrupt handler is called it needs to clear
* the allmask register. The nexus keys off of this an issues cv wakeups
* if necessary.
*/
/*
* Function prototypes for busops routines:
*/
/*
* function prototypes for dev ops routines:
*/
/*
* general function prototypes:
*/
/*
* bus ops and dev ops structures:
*/
NULL,
NULL,
NULL,
NULL,
0, /* (*bus_get_eventcookie)(); */
0, /* (*bus_add_eventcall)(); */
0, /* (*bus_remove_eventcall)(); */
0, /* (*bus_post_event)(); */
0, /* (*bus_intr_control)(); */
0, /* (*bus_config)(); */
0, /* (*bus_unconfig)(); */
0, /* (*bus_fm_init)(); */
0, /* (*bus_fm_fini)(); */
0, /* (*bus_fm_access_enter)(); */
0, /* (*bus_fm_access_exit)(); */
0, /* (*bus_power)(); */
i_ddi_intr_ops /* (*bus_intr_op(); */
};
0,
0,
(struct cb_ops *)0,
NULL,
ddi_quiesce_not_needed, /* quiesce */
};
/*
* module definitions:
*/
&mod_driverops, /* Type of module. This one is a driver */
"isadma nexus driver", /* Name of module. */
&isadma_ops, /* driver ops */
};
};
/*
* driver global data:
*/
/* Global debug data */
#ifdef DEBUG
#endif
int
_init(void)
{
int e;
/*
* Initialize per-isadma soft state pointer.
*/
sizeof (isadma_devstate_t), 1);
if (e != 0)
return (e);
/*
* Install the module.
*/
e = mod_install(&modlinkage);
if (e != 0)
return (e);
}
int
_fini(void)
{
int e;
/*
* Remove the module.
*/
e = mod_remove(&modlinkage);
if (e != 0)
return (e);
/*
* Free the soft state info.
*/
return (e);
}
int
{
}
/* device driver entry points */
/*
* attach entry point:
*/
static int
{
#ifdef DEBUG
debug_print_level = 0;
debug_info = 1;
#endif
switch (cmd) {
case DDI_ATTACH: {
/*
* Allocate soft state for this instance.
*/
!= DDI_SUCCESS) {
ret = DDI_FAILURE;
goto exit;
}
/* Cache our register property */
ret = DDI_FAILURE;
goto fail_get_prop;
}
/* Initialize our mutex */
NULL);
/* Initialize our condition variable */
goto exit;
}
case DDI_RESUME:
default:
goto exit;
}
exit:
return (ret);
}
/*
* detach entry point:
*/
static int
{
switch (cmd) {
case DDI_DETACH:
/* free the cached register property */
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
#ifdef DEBUG
static void
{
}
#endif
static void
{
/* Wait loop, if the locking dip is set, we wait. */
isadmap->isadma_want++;
isadmap->isadma_want--;
}
}
static void
{
/*
* If somebody wants register access and the lock dip is not set
* signal the waiters.
*/
}
}
/*
* Register access vectors
*/
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
{
#ifdef DEBUG
isadma_punt++;
#endif
}
#ifdef DEBUG
#endif
/* No 8 bit access to 16 bit address or count registers */
if (IN_16BIT_SPACE(offset))
goto exit;
goto exit;
exit:
return (ret);
}
/*
* Allow child devices to access this shared register set as if it were
* a real 16 bit register. The ISA bridge defines the access to this
* 16 bit dma controller & count register by programming an 8 byte register.
*/
/*ARGSUSED*/
{
#ifdef DEBUG
isadma_punt++;
#endif
}
#ifdef DEBUG
#endif
/* Only Allow access to the 16 bit count and address registers */
if (!IN_16BIT_SPACE(offset))
goto exit;
/* Set the sequencing register to the low byte */
/* Read the low byte, then high byte */
exit:
return (ret);
}
/*ARGSUSED*/
{
return (UINT32_MAX);
}
/*ARGSUSED*/
{
return (UINT64_MAX);
}
/*
* Here's where we do our locking magic. The dma all mask register is an 8
* bit register in the dma space, so we look for the access to the
* DMAC1_ALLMASK register. When somebody is masking out the dma channels
* we lock down the dma engine from further PIO accesses. When the driver
* calls back into this routine to clear the allmask register, we wakeup
* any blocked threads.
*/
/*ARGSUSED*/
void
{
#ifdef DEBUG
isadma_punt++;
#endif
return;
}
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
}
} else { /* we don't own the lock */
/* wait until on-going dma completes */
#ifdef DEBUG
#endif
}
}
/* No 8 bit access to 16 bit address or count registers */
if (IN_16BIT_SPACE(offset))
goto exit;
goto exit;
exit:
}
/*
* Allow child devices to access this shared register set as if it were
* a real 16 bit register. The ISA bridge defines the access to this
* 16 bit dma controller & count register by programming an 8 byte register.
*/
/*ARGSUSED*/
void
{
#ifdef DEBUG
isadma_punt++;
#endif
return;
}
#ifdef DEBUG
#endif
/* Only Allow access to the 16 bit count and address registers */
if (!IN_16BIT_SPACE(offset))
goto exit;
/* Set the sequencing register to the low byte */
/* Write the low byte, then the high byte */
exit:
}
/*ARGSUSED*/
void
/*ARGSUSED*/
void
/*
* The isadma_map routine determines if it's child is attempting to map a
* shared reg. If it is, it installs it's own vectors and bus private pointer
* and stacks those ops that were already defined.
*/
static int
{
int ret;
/*
* Get child regspec since the mapping struct may not have it yet
*/
return (DDI_FAILURE);
}
"parent regp %p Child reg array %p\n", (void *)child_regp,
/* Figure out if we're mapping or unmapping */
case DDI_MO_MAP_LOCKED:
/* Call up device tree to establish mapping */
if ((ret != DDI_SUCCESS) ||
break;
/* Post-process the mapping request. */
break;
case DDI_MO_UNMAP:
}
/* Call up tree to tear down mapping */
break;
default:
ret = DDI_FAILURE;
break;
}
return (ret);
}