rootnex.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Intel PC root nexus driver
* based on sun4c root nexus driver 1.30
*/
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/sysmacros.h>
#include <sys/ddidmareq.h>
#include <vm/seg_kmem.h>
#include <sys/ddi_impldefs.h>
psm_intr_op_t, int *);
extern int isa_resource_setup(void);
/* Semi-temporary patchables to phase in bug fixes */
int rootnex_bind_fail = 1;
int rootnex_bind_warn = 1;
/* bitmasks for rootnex_warn_list. Up to 8 different warnings with uint8_t */
#define ROOTNEX_BIND_WARNING (0x1 << 0)
/*
* DMA related static data
*/
static uintptr_t dvma_call_list_id = 0;
/*
* Use device arena to use for device control register mappings.
* Various kernel memory walkers (debugger, dtrace) need to know
* to avoid this address range to prevent undesired device activity.
*/
/*
* Hack to handle poke faults on Calvin-class machines
*/
extern int pokefault;
static kmutex_t pokefault_mutex;
/*
* Internal functions
*/
static int
static int
static int
/*
* config information
*/
static int
static int
static int
static int
static int
static int
static int
static int
static int
static int
static int
static struct intrspec *
static int
ddi_intr_handle_impl_t *, void *);
static struct bus_ops rootnex_bus_ops = {
NULL,
NULL,
NULL,
0, /* bus_intr_ctl */
0, /* bus_config */
0, /* bus_unconfig */
NULL, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_powr */
rootnex_intr_ops /* bus_intr_op */
};
struct priv_handle {
union {
}ph_u;
};
static uint64_t rootnex_get_phyaddr();
struct priv_handle *php);
static struct dev_ops rootnex_ops = {
0, /* refcnt */
ddi_no_info, /* info */
nulldev, /* probe */
nulldev, /* detach */
nulldev, /* reset */
0, /* cb_ops */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a nexus driver */
"i86pc root nexus %I%",
&rootnex_ops, /* Driver ops */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (EBUSY);
}
int
{
}
/*
* rootnex_attach:
*
* attach the root nexus.
*/
static void add_root_props(dev_info_t *);
/*ARGSUSED*/
static int
{
/*
* allocate array to track which major numbers we have printed warnings
* for.
*/
KM_SLEEP);
return (DDI_SUCCESS);
}
/*
* Add statically defined root properties to this list...
*/
static const int mmu_pagesize = MMU_PAGESIZE;
static const int mmu_pageoffset = MMU_PAGEOFFSET;
struct prop_def {
char *prop_name;
};
static struct prop_def root_props[] = {
};
static void
{
int i;
/*
* Note this for loop works because all of the root_prop
* properties are integers - if this changes, the for
* loop will have to change.
*/
}
/*
* Create the root node "boolean" property
* corresponding to addressing type supported in the root node:
*
* Choices are:
* "relative-addressing" (OBP PROMS)
*/
}
/*
* #define DDI_MAP_DEBUG (c.f. ddi_impl.c)
*/
#ifdef DDI_MAP_DEBUG
extern int ddi_map_debug_flag;
#endif /* DDI_MAP_DEBUG */
/*
* we don't support mapping of I/O cards above 4Gb
*/
static int
{
void *cvaddr;
#ifdef DDI_MAP_DEBUG
"rootnex_map_regspec: <0x%x 0x%x 0x%x> handle 0x%x\n",
#endif /* DDI_MAP_DEBUG */
/*
* I/O or memory mapping
*
* <bustype=0, addr=x, len=x>: memory
* <bustype=1, addr=x, len=x>: i/o
* <bustype>1, addr=0, len=x>: x86-compatibility i/o
*/
return (DDI_FAILURE);
}
if (rp->regspec_bustype != 0) {
/*
* I/O space - needs a handle.
*/
return (DDI_FAILURE);
}
#ifdef DDI_MAP_DEBUG
ddi_map_debug("rootnex_map_regspec: mmap() \
to I/O space is not supported.\n");
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_INVAL);
} else {
/*
* 1275-compliant vs. compatibility i/o mapping
*/
*vaddrp =
}
#ifdef DDI_MAP_DEBUG
"rootnex_map_regspec: \"Mapping\" %d bytes I/O space at 0x%x\n",
#endif /* DDI_MAP_DEBUG */
return (DDI_SUCCESS);
}
/*
* Memory space
*/
/*
* hat layer ignores
* hp->ah_acc.devacc_attr_endian_flags.
*/
case DDI_STRICTORDER_ACC:
break;
case DDI_UNORDERED_OK_ACC:
break;
case DDI_MERGING_OK_ACC:
break;
case DDI_LOADCACHING_OK_ACC:
break;
case DDI_STORECACHING_OK_ACC:
break;
}
} else {
}
if (rp->regspec_size == 0) {
#ifdef DDI_MAP_DEBUG
ddi_map_debug("rootnex_map_regspec: zero regspec_size\n");
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_INVAL);
}
} else {
#ifdef DDI_MAP_DEBUG
ddi_map_debug("rootnex_map_regspec: Mapping %d pages \
physical %x ",
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_NORESOURCES);
/*
* Now map in the pages we've allocated...
*/
}
#ifdef DDI_MAP_DEBUG
#endif /* DDI_MAP_DEBUG */
return (DDI_SUCCESS);
}
static int
{
return (0);
if (rp->regspec_size == 0) {
#ifdef DDI_MAP_DEBUG
ddi_map_debug("rootnex_unmap_regspec: zero regspec_size\n");
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_INVAL);
}
/*
* I/O or memory mapping:
*
* <bustype=0, addr=x, len=x>: memory
* <bustype=1, addr=x, len=x>: i/o
* <bustype>1, addr=0, len=x>: x86-compatibility i/o
*/
if (rp->regspec_bustype != 0) {
/*
* This is I/O space, which requires no particular
* processing on unmap since it isn't mapped in the
* first place.
*/
return (DDI_SUCCESS);
}
/*
* Memory space
*/
/*
* Destroy the pointer - the mapping has logically gone
*/
return (DDI_SUCCESS);
}
static int
{
#ifdef DDI_MAP_DEBUG
"rootnex_map_handle: <0x%x 0x%x 0x%x> handle 0x%x\n",
#endif /* DDI_MAP_DEBUG */
/*
* I/O or memory mapping:
*
* <bustype=0, addr=x, len=x>: memory
* <bustype=1, addr=x, len=x>: i/o
* <bustype>1, addr=0, len=x>: x86-compatibility i/o
*/
if (rp->regspec_bustype != 0) {
/*
* This refers to I/O space, and we don't support "mapping"
* I/O space to a user.
*/
return (DDI_FAILURE);
}
/*
* Set up the hat_flags for the mapping.
*/
case DDI_NEVERSWAP_ACC:
break;
case DDI_STRUCTURE_LE_ACC:
break;
case DDI_STRUCTURE_BE_ACC:
return (DDI_FAILURE);
default:
return (DDI_REGS_ACC_CONFLICT);
}
case DDI_STRICTORDER_ACC:
break;
case DDI_UNORDERED_OK_ACC:
break;
case DDI_MERGING_OK_ACC:
break;
case DDI_LOADCACHING_OK_ACC:
break;
case DDI_STORECACHING_OK_ACC:
break;
default:
return (DDI_FAILURE);
}
if (rp->regspec_size == 0)
return (DDI_ME_INVAL);
return (DDI_SUCCESS);
}
static int
{
int error;
case DDI_MO_MAP_LOCKED:
case DDI_MO_UNMAP:
case DDI_MO_MAP_HANDLE:
break;
default:
#ifdef DDI_MAP_DEBUG
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_UNIMPLEMENTED);
}
#ifdef DDI_MAP_DEBUG
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_UNIMPLEMENTED);
}
/*
* First, if given an rnumber, convert it to a regspec...
* (Presumably, this is on behalf of a child of the root node?)
*/
#ifdef DDI_MAP_DEBUG
static char *out_of_range =
"rootnex_map: Out of range rnumber <%d>, device <%s>";
#endif /* DDI_MAP_DEBUG */
#ifdef DDI_MAP_DEBUG
ddi_get_name(rdip));
#endif /* DDI_MAP_DEBUG */
return (DDI_ME_RNUMBER_RANGE);
}
/*
* Convert the given ddi_map_req_t from rnumber to regspec...
*/
}
/*
* Adjust offset and length correspnding to called values...
* XXX: A non-zero length means override the one in the regspec
* XXX: (regardless of what's in the parent's range?)
*/
#ifdef DDI_MAP_DEBUG
"rootnex: <%s,%s> <0x%x, 0x%x, 0x%d>"
" offset %d len %d handle 0x%x\n",
#endif /* DDI_MAP_DEBUG */
/*
* I/O or memory mapping:
*
* <bustype=0, addr=x, len=x>: memory
* <bustype=1, addr=x, len=x>: i/o
* <bustype>1, addr=0, len=x>: x86-compatibility i/o
*/
return (DDI_ME_INVAL);
}
/*
* compatibility i/o mapping
*/
} else {
/*
* Normal memory or i/o mapping
*/
}
if (len != 0)
#ifdef DDI_MAP_DEBUG
" <%s,%s> <0x%x, 0x%x, 0x%d>"
" offset %d len %d handle 0x%x\n",
#endif /* DDI_MAP_DEBUG */
/*
* Apply any parent ranges at this level, if applicable.
* (This is where nexus specific regspec translation takes place.
* Use of this function is implicit agreement that translation is
* provided via ddi_apply_range.)
*/
#ifdef DDI_MAP_DEBUG
ddi_map_debug("applying range of parent <%s> to child <%s>...\n",
#endif /* DDI_MAP_DEBUG */
return (error);
case DDI_MO_MAP_LOCKED:
/*
* Set up the locked down kernel mapping to the regspec...
*/
case DDI_MO_UNMAP:
/*
* Release mapping...
*/
case DDI_MO_MAP_HANDLE:
return (rootnex_map_handle(mp));
default:
return (DDI_ME_UNIMPLEMENTED);
}
}
/*
* rootnex_map_fault:
*
* fault in mappings for requestors
*/
/*ARGSUSED*/
static int
{
extern struct seg_ops segdev_ops;
#ifdef DDI_MAP_DEBUG
ddi_map_debug(" Seg <%s>\n",
#endif /* DDI_MAP_DEBUG */
/*
* This is all terribly broken, but it is a start
*
* XXX Note that this test means that segdev_ops
* must be exported from seg_dev.c.
* XXX What about devices with their own segment drivers?
*/
struct segdev_data *sdp =
/*
* This is one plausible interpretation of
* a null hat i.e. use the first hat on the
* address space hat list which by convention is
* the hat of the system MMU. At alternative
* would be to panic .. this might well be better ..
*/
}
} else
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* DMA routines- for all 80x86 machines.
*/
/*
* Shorthand defines
*/
#define MAP 0
#define BIND 1
#define FOURG 0x100000000ULL
#define SIXTEEN_MB 0x1000000
/* #define DMADEBUG */
#define DMADEBUG
static int dmadebug = 0;
#else
#define DMAPRINT(a) { }
#endif /* DEBUG */
/*
* allocate DMA handle
*/
static int
{
#ifdef lint
#endif
/*
* Validate the dma request.
*/
#ifdef DMADEBUG
DMAPRINT((" bad_limits\n"));
return (DDI_DMA_BADLIMITS);
}
#endif
/*
* validate the attribute structure. For now we do not support
* negative sgllen.
*/
(attr->dma_attr_sgllen <= 0)) {
return (DDI_DMA_BADATTR);
}
attr->dma_attr_sgllen < 0) {
return (DDI_DMA_BADATTR);
}
/*
* We will calculate a 64 bit segment size, if the segment size
* is greater that 4G, we will limit it to (4G - 1).
* The size of dma object (ddi_dma_obj_t.dmao_size)
* is 32 bits.
*/
else
/*
* We should be able to DMA into every byte offset in a page.
*/
if (maxsegmentsize < MMU_PAGESIZE) {
DMAPRINT((" bad_limits, maxsegmentsize\n"));
return (DDI_DMA_BADLIMITS);
}
if (waitfp != DDI_DMA_DONTWAIT) {
}
return (DDI_DMA_NORESOURCES);
}
/*
* Preallocate space for cookie structures. We will use this when
* the request does not span more than (DMAI_SOMEMORE_COOKIES - 1)
* pages.
*/
/*
* Save requestor's information
*/
hp->dmai_kaddr =
hp->dmai_inuse = 0;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
/*
* free the additional cookie space.
*/
if (hp->dmai_additionalcookiep)
sizeof (ddi_dma_cookie_t) * DMAI_SOMEMORE_COOKIES);
if (dvma_call_list_id)
return (DDI_SUCCESS);
}
static int
{
int rval;
struct priv_handle php;
/*
* no mutex for speed
*/
if (hp->dmai_inuse) {
return (DDI_DMA_INUSE);
}
/*
* get the physical address of the first page of an object
* defined through 'dmareq' structure.
*/
DMAPRINT((" bad_limits/mapping\n"));
return (DDI_DMA_NOMAPPING);
/*
* The object is not more than a PAGESIZE and we could DMA into
* the physical page.
* The cache is completely coherent, set the NOSYNC flag.
*/
/*
* Fill in the physical address in the cookie pointer.
*/
*ccountp = 1;
} else if (hp->dmai_additionalcookiep) {
/*
* The object spans a page boundary. We will use the space
* that we preallocated to store the additional cookie.
*/
/*
* We can not DMA into this physical page. We will
* need intermediate buffers. Reset the state in
* the php structure.
*/
goto io_brkup_attr;
}
*ccountp = 2;
} else {
goto io_brkup_attr;
}
return (DDI_DMA_MAPPED);
}
/*
* The function rootnex_get_phyaddr() does not save the physical
* address in the php structure. Save it here for
* rootnext_io_brkup_attr().
*/
hp->dmai_inuse = 0;
return (rval);
}
if (hp->dmai_ibufp) {
}
segcount++;
}
hp->dmai_cookie++;
/*
* If we ended up with more cookies that the caller specified as
* the maximum that it can handle (sgllen), and they didn't specify
* DDI_DMA_PARTIAL, cleanup and return failure.
*
* Not the cleanest fix, but lowest risk. The DMA code in
* this file should get a good cleaning for some performance
* improvement. This should be cleaned up also during that work.
*/
/*
* patchable which allows us to print one warning per major
* number.
*/
if ((rootnex_bind_warn) &&
"driver is using ddi_dma_attr(9S) incorrectly. "
"There is a small risk of data corruption in "
"replaced with a corrected version for proper "
"system operation. To disable this warning, add "
"'set rootnex:rootnex_bind_warn=0' to "
}
/*
* Patchable which allows us to fail or pass the bind. The
* correct behavior should be to fail the bind. To be safe for
* now, the patchable allows the previous behavior to be set
*/
if (rootnex_bind_fail) {
if (hp->dmai_ibufp)
if (hp->dmai_kaddr)
hp->dmai_inuse = 0;
*ccountp = 0;
return (DDI_DMA_TOOBIG);
}
}
return (rval);
}
/*ARGSUSED*/
static int
{
int rval = DDI_SUCCESS;
if (hp->dmai_ibufp) {
}
if (hp->dmai_kaddr)
if (dvma_call_list_id)
hp->dmai_inuse = 0;
return (rval);
}
/*ARGSUSED*/
static int
{
int rval = DDI_SUCCESS;
if (hp->dmai_ibufp) {
if (cache_flags == DDI_DMA_SYNC_FORDEV) {
} else {
}
}
return (rval);
}
/*ARGSUSED*/
static int
{
int i;
/*
* win is in the range [0 .. dmai_nwin-1]
*/
return (DDI_FAILURE);
}
(void) rootnex_io_rdsync(hp);
}
for (i = 0; i < win; i++) {
}
if (hp->dmai_ibufp)
segcount++;
}
hp->dmai_cookie++;
DMAPRINT(("getwin win %p mapping %llx size %lx\n",
return (DDI_SUCCESS);
}
static int
{
struct priv_handle php;
int sizehandle;
int mapinfo;
#ifdef lint
#endif
#ifdef DMADEBUG
/*
* Validate range checks on DMA limits
*/
dma_lim->dlim_sgllen <= 0) {
DMAPRINT((" bad_limits\n"));
return (DDI_DMA_BADLIMITS);
}
#endif
/*
* get the physical address of the first page of an object
* defined through 'dmareq' structure.
*/
DMAPRINT((" bad_limits/mapping\n"));
return (DDI_DMA_NOMAPPING);
/*
* The object is less than a PAGESIZE and we could DMA into
* the physical page.
*/
if (!handlep)
return (DDI_DMA_MAPOK);
sizehandle = sizeof (ddi_dma_impl_t) +
sizeof (impl_dma_segment_t);
KM_SLEEP : KM_NOSLEEP);
if (!hp) {
/* let other routine do callback */
goto breakup_req;
}
/*
* locate segments after dma_impl handle structure
*/
/* FMA related initialization */
hp->dmai_fault = 0;
/*
* Save requestor's information
*/
if (mapinfo == DMAMI_PAGES) {
} else {
segmentp->dmais_ofst = 0;
}
return (DDI_DMA_MAPPED);
} else if (!handlep) {
return (DDI_DMA_NOMAPPING);
}
/*
* The function rootnex_get_phyaddr() does not save the physical
* address in the php structure. Save it here for
* rootnext_io_brkup_attr().
*/
}
/* CSTYLED */
((psegp) && \
(((flg) & DMAIS_NEEDINTBUF) == 0) && \
/* CSTYLED */
/*
* This function works with the ddi_dma_attr structure.
* Bugs fixed
* 1. The old code would ignore the size of the first segment when
* computing the total size of the reuqest (sglistsize) for sgllen == 1
*/
/*ARGSUSED*/
int
struct priv_handle *php)
{
int nsegments;
int mapinfo;
int reqneedintbuf;
int rval;
int segment_flags, win_flags;
int sgcount;
int wcount;
int sizehandle;
#ifdef lint
#endif
/*
* Initialize our local variables from the php structure.
* rootnex_get_phyaddr() has populated php structure on its
* previous invocation in rootnex_dma_bindhdl().
*/
/*
* maxsegmentsize was computed and saved in rootnex_dma_allochdl().
*/
/*
* The number of segments is the number of 4k pages that the
* object spans.
* Each 4k segment may need another segment to satisfy
* device granularity reqirements.
* We will never need more than two segments per page.
* This may be an overestimate in some cases but it avoids
* 64 bit divide operations.
*/
(MMU_PAGESHIFT - 1);
sizeof (ddi_dma_cookie_t));
goto bad;
}
/*
* Breakup the memory object
* and build an i/o segment at each boundary condition
*/
curwinp = 0;
needintbuf = 0;
previousp = 0;
reqneedintbuf = 0;
sglistsize = 0;
wcount = 0;
sgcount = 1;
do {
/*
* limit the number of bytes to dma_attr_maxxfer
*/
sizesegment -=
}
(residual_size != sizesegment)) {
/*
* so ensure that size of each segment is a
* multiple of dma_attr_granular (== sector size)
*/
}
} else {
/*
* If we can not combine this segment with the
* previous segment or if there are no previous
* segments, sglistsize should be set to
* segmentsize.
*/
if (previousp) {
}
if (curwinp == 0) {
wcount++;
} else {
}
if (mapinfo == DMAMI_PAGES)
else
cookiep++;
--nsegments;
sgcount++;
if (segment_flags & DMAIS_NEEDINTBUF) {
== MAX_INT_BUF) {
/*
* Intermediate buffers need not be contiguous.
* we allocate a page of intermediate buffer
* for every segment.
*/
needintbuf = 0;
}
}
}
sgcount = 1;
if ((sizesegment != residual_size) &&
(trim == sizesegment)) {
/*
* Normally we would trim the buffer to make it a
* multiple of the granularity. But in this case,
* the size is < the granularity so we'll roll back
* this segment and pick this up the next time around.
*
* This case occurs when sgcount naturally (i.e. not
* forced) is greater than > dma_attr_sgllen. In this
* case, if the very next segment fills up the
* intermediate buffer, and the amount required to fill
* the intermediate buffer < granularity, we would end
* up with a zero sized cookie if we didn't roll back
* the segment.
*/
/*
* Make sure we really understand the code path here,
* we should only get here if we are at an end of a
* window which is a single page long < granularity
*/
/* Zero out this segment and add it back to the count */
sizesegment = 0;
sglistsize = 0;
nsegments++;
/* fix the segment and cookie pointers */
previousp--;
cookiep--;
/*
* cleanup the new previous pointer. Make sure we
* carry over the WINEND maker.
*/
/*
* ensure that total length of list is a
* multiple of granular (sector size)
*/
sizesegment -= trim;
}
sglistsize = 0;
}
/*
* Get the physical address of the next page in the
* dma object.
*/
}
} while (residual_size && nsegments);
ASSERT(residual_size == 0);
if (curwinp) {
if (win_flags & DMAIS_NEEDINTBUF)
} else
/*
* Allocate intermediate buffer. To start with we request
* for a page aligned area. This request is satisfied from
* the system page free list pool.
*/
NULL) != DDI_SUCCESS) {
goto bad;
}
if (mapinfo != DMAMI_KVADR) {
VM_SLEEP);
}
}
/*
* return success
*/
if (wcount == 1) {
} else {
return (DDI_DMA_TOOBIG);
}
return (rval);
bad:
if (rval == DDI_DMA_NORESOURCES &&
return (rval);
}
/*
* This function works with the limit structure and does 32 bit arithmetic.
*/
int
{
ddi_dma_impl_t *hp = 0;
int nsegments;
int mapinfo;
int reqneedintbuf;
int rval;
int segment_flags, win_flags;
int sgcount;
int wcount;
#ifdef DMADEBUG
int numsegments;
#endif
int sizehandle;
#ifdef lint
#endif
/*
* Validate the dma request.
*/
#ifdef DMADEBUG
DMAPRINT((" bad_limits\n"));
return (DDI_DMA_BADLIMITS);
}
#endif
/*
* Initialize our local variables from the php structure.
* rootnex_get_phyaddr() has populated php structure on its
* previous invocation in rootnex_dma_map().
*/
if (dma_lim->dlim_sgllen <= 0 ||
DMAPRINT((" bad_limits/mapping\n"));
goto bad;
}
if (maxsegmentsize == 0)
if (maxsegmentsize < MMU_PAGESIZE) {
DMAPRINT((" bad_limits, maxsegmentsize\n"));
goto bad;
}
/*
* The number of segments is the number of 4k pages that the
* object spans.
* Each 4k segment may need another segment to satisfy
* device granularity reqirements.
* We will never need more than two segments per page.
* This may be an overestimate in some cases but it avoids
* 64 bit divide operations.
*/
(MMU_PAGESHIFT - 1);
#ifdef DMADEBUG
#endif
sizehandle = sizeof (ddi_dma_impl_t) +
(nsegments * sizeof (impl_dma_segment_t));
if (!hp) {
goto bad;
}
/*
* locate segments after dma_impl handle structure
*/
/* FMA related initialization */
hp->dmai_fault = 0;
/*
* Save requestor's information
*/
/*
* Breakup the memory object
* and build an i/o segment at each boundary condition
*/
curwinp = 0;
needintbuf = 0;
previousp = 0;
reqneedintbuf = 0;
sglistsize = 0;
wcount = 0;
sgcount = 1;
do {
/*
* so ensure that size of each segment is a
* multiple of dlim_granular (== sector size)
*/
residual_size != sizesegment) {
/*
* this segment needs an intermediate buffer
*/
}
}
if (previousp &&
segmentpadr &&
(previousp->dmais_flags &
(DMAIS_NEEDINTBUF | DMAIS_COMPLEMENT)) == 0 &&
(segment_flags & DMAIS_NEEDINTBUF) == 0 &&
/*
* combine new segment with previous segment
*/
if ((sglistsize += sizesegment) ==
/*
*/
} else {
/*
* add new segment to linked list
*/
if (previousp) {
}
if (curwinp == 0) {
wcount++;
} else {
}
if (mapinfo == DMAMI_PAGES) {
} else {
}
if (segment_flags & DMAIS_NEEDINTBUF) {
if (needintbuf >= MAX_INT_BUF) {
/*
* limit size of intermediate
* buffer
*/
needintbuf = 0;
/*
* end of current window
*/
segmentp->dmais_flags |=
curwinp->dmais_flags |=
/*
* list
*/
}
}
/*
* limit size of xfer
*/
sizesegment -= (sglistsize -
}
sgcount++;
} else {
/*
*/
if (segment_flags & DMAIS_NEEDINTBUF) {
/*
* end of window
*/
}
}
--nsegments;
}
/*
* ensure that total length of list is a
* multiple of granular (sector size)
*/
if (sizesegment != residual_size) {
trim = sglistsize &
if (trim >= sizesegment) {
"unable to reduce segment size");
goto bad;
}
sizesegment -= trim;
sgcount = 1;
sglistsize = 0;
}
}
}
} while (residual_size && nsegments);
ASSERT(residual_size == 0);
if (curwinp) {
if (win_flags & DMAIS_NEEDINTBUF)
} else
dma_attr.dma_attr_flags = 0;
/*
* Allocate intermediate buffer.
*/
NULL) != DDI_SUCCESS) {
goto bad;
}
if (mapinfo != DMAMI_KVADR) {
VM_SLEEP);
}
}
/*
* return success
*/
#ifdef DMADEBUG
DMAPRINT(("dma_brkup: handle %p nsegments %x \n",
#endif
return (DDI_DMA_MAPPED);
bad:
if (hp)
if (rval == DDI_DMA_NORESOURCES &&
return (rval);
}
int
{
int cpycnt;
}
return (DDI_SUCCESS);
case DMAMI_KVADR:
/*
* copy from segment to buffer
*/
sp->dmais_size);
/*
* save phys addr of intermediate buffer
*/
}
addr += MMU_PAGESIZE;
break;
case DMAMI_PAGES:
/*
* need to mapin page so we can have a
* virtual address to do copying
*/
/*
* copy from segment to buffer
*/
/*
* need to mapout page
*/
}
/*
* save phys addr of intemediate buffer
*/
}
addr += MMU_PAGESIZE;
break;
case DMAMI_UVADR:
segoffset = 0;
do {
/*
* need to mapin page so we can have a
* virtual address to do copying
*/
vsoffset =
/*
* check if we have to use the
* shadow list or the CPU mapping.
*/
hp->dmai_kaddr);
} else {
hp->dmai_kaddr);
}
cpycnt = MMU_PAGESIZE -
/*
* copy from segment to buffer
*/
cpycnt);
/*
* need to mapout page
*/
}
/*
* save phys addr of intermediate buffer
*/
}
addr += MMU_PAGESIZE;
break;
default:
}
return (DDI_SUCCESS);
}
int
{
int cpycnt;
}
return (DDI_SUCCESS);
case DMAMI_KVADR:
/*
* copy from buffer to segment
*/
addr += MMU_PAGESIZE;
break;
case DMAMI_PAGES:
/*
* need to mapin page
*/
/*
* copy from buffer to segment
*/
(hp->dmai_kaddr +
sp->dmais_size);
/*
* need to mapout page
*/
addr += MMU_PAGESIZE;
break;
case DMAMI_UVADR:
segoffset = 0;
do {
/*
* need to map_in user virtual address
*/
/*
* check if we have to use the
* shadow list or the CPU mapping.
*/
hp->dmai_kaddr);
} else {
hp->dmai_kaddr);
}
/*
* copy from buffer to segment
*/
/*
* need to map_out page
*/
addr += MMU_PAGESIZE;
break;
default:
}
return (DDI_SUCCESS);
}
static int
{
#if !defined(__amd64)
#endif
int rval = DDI_SUCCESS;
#ifdef lint
#endif
switch (request) {
case DDI_DMA_SEGTOC:
#if defined(__amd64)
/*
* ddi_dma_segtocookie(9F) is Obsolete, and the whole
* passing-the-pointer-through-the-cache-flags thing just
* doesn't work when pointers are 64-bit and cache_flags
* are 32-bit.
*/
DMAPRINT(("stoc invoked but not implemented.\n"));
return (DDI_FAILURE);
#else
/* return device specific dma cookie for segment */
if (!sp) {
return (DDI_FAILURE);
}
/*
* use phys addr of actual buffer or intermediate buffer
*/
DMAPRINT(("stoc segment %p mapping %lx size %lx\n",
return (DDI_SUCCESS);
#endif
case DDI_DMA_NEXTSEG: /* get next DMA segment */
DMAPRINT(("nxseg: not current window %p\n",
(void *)wp));
return (DDI_DMA_STALE);
}
if (!sp) {
/*
* reset to first segment in current window
*/
} else {
return (DDI_DMA_DONE);
}
/* check if segment is really in window */
}
return (DDI_SUCCESS);
case DDI_DMA_NEXTWIN: /* get next DMA window */
/*
* do implied sync on current window
*/
(void) rootnex_io_rdsync(hp);
if (!wp) {
/*
* reset to (first segment of) first window
*/
} else {
DMAPRINT(("nxwin: win %p not current\n",
(void *)wp));
return (DDI_DMA_STALE);
}
return (DDI_DMA_DONE);
}
}
if (hp->dmai_ibufp)
return (DDI_SUCCESS);
case DDI_DMA_FREE:
DMAPRINT(("free handle\n"));
if (hp->dmai_ibufp) {
}
if (hp->dmai_kaddr)
if (dvma_call_list_id)
break;
case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */
DMAPRINT(("iopb alloc\n"));
break;
case DDI_DMA_SMEM_ALLOC: /* get contiguous DMA-able memory */
DMAPRINT(("mem alloc\n"));
break;
case DDI_DMA_KVADDR:
DMAPRINT(("kvaddr of phys mapping\n"));
return (DDI_FAILURE);
case DDI_DMA_GETERR:
DMAPRINT(("geterr\n"));
rval = DDI_FAILURE;
break;
case DDI_DMA_COFF:
DMAPRINT(("coff off %p mapping %llx size %lx\n",
(void *)*objpp,
rval = DDI_FAILURE;
break;
default:
return (DDI_FAILURE);
}
return (rval);
}
/*
* Root nexus ctl functions
*/
#define REPORTDEV_BUFSIZE 1024
static int
{
char *buf;
for (i = 0; i < sparc_pd_getnreg(dev); i++) {
if (i == 0)
": ");
else
" and ");
switch (rp->regspec_bustype) {
case BTEISA:
break;
case BTISA:
break;
default:
"space %x offset %x",
break;
}
}
for (i = 0, n = sparc_pd_getnintr(dev); i < n; i++) {
int pri;
if (i != 0) {
",");
}
" sparc ipl %d", pri);
}
#ifdef DEBUG
"printed length 1024, real length %d", f_len);
}
#endif /* DEBUG */
return (DDI_SUCCESS);
}
/*
* For the x86 rootnexus, we're prepared to claim that the interrupt string
* is in the form of a list of <ipl,vec> specifications.
*/
#define VEC_MIN 1
#define VEC_MAX 255
static int
struct ddi_parent_private_data *pdptr)
{
int n;
int *inpri;
int got_len;
extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */
static char bad_intr_fmt[] =
"rootnex: bad interrupt spec from %s%d - ipl %d, irq %d\n";
#ifdef lint
#endif
/*
* determine if the driver is expecting the new style "interrupts"
* property which just contains the IRQ, or the old style which
* contains pairs of <IPL,IRQ>. if it is the new style, we always
* assign IPL 5 unless an "interrupt-priorities" property exists.
* in that case, the "interrupt-priorities" property contains the
* IPL values that match, one for one, the IRQ values in the
* "interrupts" property.
*/
"ignore-hardware-nodes", -1) != -1) ||
/* the old style "interrupts" property... */
/*
* The list consists of <ipl,vec> elements
*/
return (DDI_FAILURE);
while (n--) {
goto broken;
}
if (vec != 2)
else
/*
* irq 2 on the PC bus is tied to irq 9
* on ISA, EISA and MicroChannel
*/
new++;
}
return (DDI_SUCCESS);
} else {
/* the new style "interrupts" property... */
/*
* The list consists of <vec> elements
*/
if ((n = (*in++)) < 1)
return (DDI_FAILURE);
/* XXX check for "interrupt-priorities" property... */
== DDI_PROP_SUCCESS) {
if (n != (got_len / sizeof (int))) {
"rootnex: bad interrupt-priorities length"
" from %s%d: expected %d, got %d\n",
(int)(got_len / sizeof (int)));
goto broken;
}
}
while (n--) {
int level;
level = 5;
else
goto broken;
}
if (vec != 2)
else
/*
* irq 2 on the PC bus is tied to irq 9
* on ISA, EISA and MicroChannel
*/
new++;
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
{
extern int impl_ddi_sunbus_initchild(dev_info_t *);
extern void impl_ddi_sunbus_removechild(dev_info_t *);
switch (ctlop) {
case DDI_CTLOPS_INITCHILD:
return (impl_ddi_sunbus_initchild(child));
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int err = DDI_SUCCESS;
/* Cautious access not supported. */
return (DDI_FAILURE);
pokefault = -1;
/* Set up protected environment. */
case sizeof (uint8_t):
break;
case sizeof (uint16_t):
break;
case sizeof (uint32_t):
break;
case sizeof (uint64_t):
break;
default:
err = DDI_FAILURE;
break;
}
} else
err = DDI_FAILURE;
/* Take down protected environment. */
no_trap();
pokefault = 0;
return (err);
}
static int
{
int err = DDI_SUCCESS;
/* Cautious access not supported. */
return (DDI_FAILURE);
case sizeof (uint8_t):
break;
case sizeof (uint16_t):
break;
case sizeof (uint32_t):
break;
case sizeof (uint64_t):
break;
default:
err = DDI_FAILURE;
break;
}
} else
err = DDI_FAILURE;
no_trap();
return (err);
}
static int
{
int n, *ptr;
struct ddi_parent_private_data *pdp;
switch (ctlop) {
case DDI_CTLOPS_DMAPMAPC:
/*
* Return 'partial' to indicate that dma mapping
* has to be done in the main MMU.
*/
return (DDI_DMA_PARTIAL);
case DDI_CTLOPS_BTOP:
/*
* Convert byte count input to physical page units.
* (byte counts that are not a page-size multiple
* are rounded down)
*/
return (DDI_SUCCESS);
case DDI_CTLOPS_PTOB:
/*
* Convert size in physical pages to bytes
*/
return (DDI_SUCCESS);
case DDI_CTLOPS_BTOPR:
/*
* Convert byte count input to physical page units
* (byte counts that are not a page-size multiple
* are rounded up)
*/
return (DDI_SUCCESS);
case DDI_CTLOPS_POKE:
case DDI_CTLOPS_PEEK:
case DDI_CTLOPS_INITCHILD:
case DDI_CTLOPS_UNINITCHILD:
case DDI_CTLOPS_REPORTDEV:
return (rootnex_ctl_reportdev(rdip));
case DDI_CTLOPS_IOMIN:
/*
* Nothing to do here but reflect back..
*/
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
case DDI_CTLOPS_NINTRS:
break;
case DDI_CTLOPS_SIDDEV:
if (ndi_dev_is_prom_node(rdip))
return (DDI_SUCCESS);
return (DDI_SUCCESS);
return (DDI_FAILURE);
case DDI_CTLOPS_INTR_HILEVEL:
/*
* Indicate whether the interrupt specified is to be handled
* above lock level. In other words, above the level that
* cv_signal and default type mutexes can be used.
*/
*(int *)result =
> LOCK_LEVEL);
return (DDI_SUCCESS);
case DDI_CTLOPS_XLATE_INTRS:
case DDI_CTLOPS_POWER:
case DDI_CTLOPS_RESERVED1: /* Was DDI_CTLOPS_POKE_INIT, obsolete */
case DDI_CTLOPS_RESERVED2: /* Was DDI_CTLOPS_POKE_FLUSH, obsolete */
case DDI_CTLOPS_RESERVED3: /* Was DDI_CTLOPS_POKE_FINI, obsolete */
if (!reserved_msg_printed) {
}
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
/*
* The rest are for "hardware" properties
*/
return (DDI_FAILURE);
if (ctlop == DDI_CTLOPS_NREGS) {
} else if (ctlop == DDI_CTLOPS_NINTRS) {
} else {
n = *ptr;
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* rootnex_get_ispec:
* convert an interrupt number to an interrupt specification.
* The interrupt number determines which interrupt spec will be
* returned if more than one exists.
*
* Look into the parent private data area of the 'rdip' to find out
* the interrupt specification. First check to make sure there is
* one that matchs "inumber" and then return a pointer to it.
*
* Return NULL if one could not be found.
*
* NOTE: This is needed for rootnex_intr_ops()
*/
static struct intrspec *
{
/*
* Special case handling for drivers that provide their own
* intrspec structures instead of relying on the DDI framework.
*
* A broken hardware driver in ON could potentially provide its
* own intrspec structure, instead of relying on the hardware.
* If these drivers are children of 'rootnex' then we need to
* continue to provide backward compatibility to them here.
*
* Following check is a special case for 'pcic' driver which
* was found to have broken hardwre andby provides its own intrspec.
*
* Verbatim comments from this driver are shown here:
* "Don't use the ddi_add_intr since we don't have a
* default intrspec in all cases."
*
* Since an 'ispec' may not be always created for it,
* check for that and create one if so.
*
* NOTE: Currently 'pcic' is the only driver found to do this.
*/
}
/* Validate the interrupt number */
return (NULL);
/* Get the interrupt structure pointer and return that */
}
/*
* rootnex_intr_ops:
* bus_intr_op() function for interrupt support
*/
/* ARGSUSED */
static int
{
struct ddi_parent_private_data *pdp;
"rootnex_intr_ops: pdip = %p, rdip = %p, intr_op = %x, hdlp = %p\n",
/* Process the interrupt operation */
switch (intr_op) {
case DDI_INTROP_GETCAP:
/* First check with pcplusmp */
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
*(int *)result = 0;
return (DDI_FAILURE);
}
break;
case DDI_INTROP_SETCAP:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
return (DDI_FAILURE);
break;
case DDI_INTROP_ALLOC:
return (DDI_FAILURE);
break;
case DDI_INTROP_FREE:
/*
* Special case for 'pcic' driver' only.
* If an intrspec was created for it, clean it up here
* See detailed comments on this in the function
* rootnex_get_ispec().
*/
/*
* Set it to zero; so that
* DDI framework doesn't free it again
*/
}
break;
case DDI_INTROP_GETPRI:
return (DDI_FAILURE);
break;
case DDI_INTROP_SETPRI:
/* Validate the interrupt priority passed to us */
if (*(int *)result > LOCK_LEVEL)
return (DDI_FAILURE);
/* Ensure that PSM is all initialized and ispec is ok */
if ((psm_intr_ops == NULL) ||
return (DDI_FAILURE);
/* Change the priority */
return (DDI_FAILURE);
/* update the ispec with the new priority */
break;
case DDI_INTROP_ADDISR:
return (DDI_FAILURE);
break;
case DDI_INTROP_REMISR:
return (DDI_FAILURE);
break;
case DDI_INTROP_ENABLE:
return (DDI_FAILURE);
/* Call psmi to translate irq with the dip */
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
/* Add the interrupt handler */
return (DDI_FAILURE);
break;
case DDI_INTROP_DISABLE:
return (DDI_FAILURE);
/* Call psm_ops() to translate irq with the dip */
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
/* Remove the interrupt handler */
break;
case DDI_INTROP_SETMASK:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
return (DDI_FAILURE);
break;
case DDI_INTROP_CLRMASK:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
return (DDI_FAILURE);
break;
case DDI_INTROP_GETPENDING:
if (psm_intr_ops == NULL)
return (DDI_FAILURE);
result)) {
*(int *)result = 0;
return (DDI_FAILURE);
}
break;
case DDI_INTROP_NINTRS:
return (DDI_FAILURE);
/*
* Special case for 'pcic' driver' only. This driver
* driver is a child of 'isa' and 'rootnex' drivers.
*
* See detailed comments on this in the function
* rootnex_get_ispec().
*
* Children of 'pcic' send 'NINITR' request all the
* way to rootnex driver. But, the 'pdp->par_nintr'
* field may not initialized. So, we fake it here
* to return 1 (a la what PCMCIA nexus does).
*/
*(int *)result = 1;
}
break;
*(int *)result = 0;
break;
case DDI_INTROP_NAVAIL:
return (DDI_FAILURE);
if (psm_intr_ops == NULL) {
*(int *)result = 1;
break;
}
/* Priority in the handle not initialized yet */
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Get the physical address of an object described by "dmareq".
* A "segsize" of zero is used to initialize the priv_handle *php.
* Subsequent calls with a non zero "segsize" would get the corresponding
* physical address of the dma object.
* The function returns a 64 bit physical address.
*/
struct priv_handle *php)
{
int index;
case DMA_OTYP_PAGES:
if (segsize) {
/*
* crossed page boundary, get to the next page.
*/
offset &= MMU_PAGEOFFSET;
}
} else {
/*
* Initialize the priv_handle structure.
*/
}
break;
case DMA_OTYP_VADDR:
case DMA_OTYP_BUFVADDR:
if (segsize) {
} else {
/*
* Initialize the priv_handle structure.
*/
} else {
}
}
} else {
}
break;
default:
panic("rootnex_get_phyaddr");
/*NOTREACHED*/
}
return (segmentpadr);
}