busra.c revision a3282898e99eb4fd1912bf791254452bfd913d4b
/*
* 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"
#if defined(DEBUG)
#define BUSRA_DEBUG
#endif
/*
* This module provides a set of resource management interfaces
* to manage bus resources globally in the system.
*
* The bus nexus drivers are typically responsible to setup resource
* maps for the bus resources available for a bus instance. However
* this module also provides resource setup functions for PCI bus
* (used by both SPARC and X86 platforms) and ISA bus instances (used
* only for X86 platforms).
*/
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/autoconf.h>
#if defined(BUSRA_DEBUG)
int busra_debug = 0;
#define DEBUGPRT \
if (busra_debug) cmn_err
#else
#define DEBUGPRT \
if (0) cmn_err
#endif
/*
* global mutex that protects the global list of resource maps.
*/
/*
* basic resource element
*/
struct ra_resource {
struct ra_resource *ra_next;
};
/*
* link list element for the list of dips (and their resource ranges)
* for a particular resource type.
* ra_rangeset points to the list of resources available
* for this type and this dip.
*/
struct ra_dip_type {
struct ra_dip_type *ra_next;
struct ra_resource *ra_rangeset;
};
/*
* link list element for list of types resources. Each element
* has all resources for a particular type.
*/
struct ra_type_map {
struct ra_type_map *ra_next;
struct ra_dip_type *ra_dip_list;
char *type;
};
/*
* place holder to keep the head of the whole global list.
* the address of the first typemap would be stored in it.
*/
/*
* This is the loadable module wrapper.
* It is essentially boilerplate so isn't documented
*/
extern struct mod_ops mod_miscops;
#ifdef BUSRA_DEBUG
void ra_dump_all();
#endif
/* internal function prototypes */
&mod_miscops, /* Type of module. This one is a module */
"Bus Resource Allocator (BUSRA) %I%", /* Name of the module. */
};
static struct modlinkage modlinkage = {
};
int
_init()
{
int ret;
}
return (ret);
}
int
_fini()
{
int ret;
if (ra_map_list_head != NULL) {
return (EBUSY);
}
if (ret == 0)
return (ret);
}
int
{
}
/*
* set up an empty resource map for a given type and dip
*/
int
{
struct ra_type_map *typemapp;
struct ra_dip_type *dipmap;
struct ra_dip_type **backdip;
struct ra_type_map **backtype;
typemapp = (struct ra_type_map *)
KM_SLEEP);
} else {
}
/* allocate and insert in list of dips for this type */
dipmap = (struct ra_dip_type *)
}
}
return (NDI_SUCCESS);
}
/*
* destroys a resource map for a given dip and type
*/
int
{
struct ra_dip_type *dipmap;
struct ra_dip_type **backdip;
struct ra_resource *range;
return (NDI_FAILURE);
}
/*
* destroy all resources for this dip
* remove dip from type list
*/
}
/* remove from dip list */
/*
* This was the last dip with this resource type.
* Remove the type from the global list.
*/
}
return (NDI_SUCCESS);
}
static int
{
struct ra_dip_type **backdip;
struct ra_type_map **backtype;
return (NDI_FAILURE);
}
return (NDI_SUCCESS);
}
/*
* Find a dip map for the specified type, if NDI_RA_PASS will go up on dev tree
* if found, backdip and backtype will be updated to point to the previous
* dip in the list and previous type for this dip in the list.
* If no such type at all in the resource list both backdip and backtype
* will be null. If the type found but no dip, back dip will be null.
*/
static struct ra_dip_type *
{
struct ra_type_map **prevmap;
while (*prevmap) {
break;
}
if (*prevmap) {
while (dipmap) {
break;
}
/* found it */
break;
}
if (!(flag & NDI_RA_PASS)) {
break;
}
}
}
return (dipmap);
}
int
{
struct ra_dip_type *dipmap;
struct ra_dip_type **backdip;
struct ra_type_map **backtype;
if (len == 0) {
return (NDI_SUCCESS);
}
return (NDI_FAILURE);
}
/* now find where range lies and fix things up */
/* check for overlap first */
/* overlap with mapp */
overlapmap = mapp;
goto overlap;
/* overlap with mapp->ra_next */
goto overlap;
}
/* simple - on front */
/*
* don't need to check if it merges with
* previous since that would match on on end
*/
break;
/* simple - on end */
/* merge with next node */
}
break;
/* somewhere in between so just an insert */
newmap = (struct ra_resource *)
break;
}
}
/* stick on end */
newmap = (struct ra_resource *)
}
return (NDI_SUCCESS);
/*
* Bad free may happen on some x86 platforms with BIOS exporting
* incorrect resource maps. The system is otherwise functioning
* normally. We send such messages to syslog only.
*/
overlapmap->ra_len);
return (NDI_FAILURE);
}
/* check to see if value is power of 2 or not. */
static int
{
/*
* ddi_ffs and ddi_fls gets long values, so in 32bit environment
* won't work correctly for 64bit values
*/
return (0);
return (1);
}
static void
{
struct ra_resource *newmap;
/* in the middle or end */
/* on the end */
} else {
/* in the middle */
newmap = (struct ra_resource *)
}
} else {
/* at the beginning */
/* remove the whole node */
}
}
}
int
{
struct ra_dip_type *dipmap;
struct ra_dip_type **backdip;
struct ra_type_map **backtype;
int rval = NDI_FAILURE;
*retbasep = 0;
*retlenp = 0;
return (NDI_FAILURE);
}
}
return (NDI_FAILURE);
}
backlargestp = NULL;
largestbase = 0;
largestlen = 0;
lower = 0;
/* bounded so skip to first possible */
/*
* This elements end goes beyond max uint64_t.
* potential candidate, check end against lower
* would not be precise.
*/
break;
}
}
/* first fit - not user specified */
}
/*
* failed a critical constraint
* adjust and see if it still fits
*/
base);
/*
* Check to see if the new base is past
* the end of the resource.
*/
continue;
}
}
else
if ((backlargestp == NULL) ||
(largestlen < remlen)) {
largestbase = base;
largestlen = remlen;
}
}
/* a candidate -- apply constraints */
continue;
}
/* we have a fit */
rval = NDI_SUCCESS;
break;
}
}
} else {
/*
* This is the node with he requested base in
* its range
*/
/* length requirement not satisfied */
else
remlen =
(base -
}
largestbase = base;
largestlen = remlen;
base = 0;
} else {
/* We have a match */
rval = NDI_SUCCESS;
}
break;
}
}
}
if ((rval != NDI_SUCCESS) &&
(backlargestp != NULL)) {
base = largestbase;
len = largestlen;
}
if (rval == NDI_FAILURE) {
*retbasep = 0;
*retlenp = 0;
} else {
}
return (rval);
}
/*
* isa_resource_setup
* check for /used-resources and initialize
* based on info there. If no /used-resources,
* fail.
*/
int
{
/*
* note that at this time bootconf creates 32 bit properties for
* io-space and device-memory
*/
struct iorange {
} *iorange;
struct memrange {
} *memrange;
int proplen;
int i, len;
int maxrange;
"isa_resource_setup: used-resources not found");
return (NDI_FAILURE);
}
/*
* initialize to all resources being present
* and then remove the ones in use.
*/
usedpdip = ddi_root_node();
return (NDI_FAILURE);
}
/* initialize io space, highest end base is 0xffff */
/* note that length is highest addr + 1 since starts from 0 */
/* remove the "used" I/O resources */
for (i = 0; i < maxrange; i++) {
NDI_RA_TYPE_IO, 0);
}
}
return (NDI_FAILURE);
}
/* initialize memory space where highest end base is 0xffffffff */
/* note that length is highest addr + 1 since starts from 0 */
NDI_RA_TYPE_MEM, 0);
/* remove the "used" memory resources */
for (i = 0; i < maxrange; i++) {
NDI_RA_TYPE_MEM, 0);
}
}
return (NDI_FAILURE);
}
/* initialize the interrupt space */
NDI_RA_TYPE_INTR, 0);
#endif
/* Initialize available interrupts by negating the used */
for (i = 0; i < len; i++) {
NDI_RA_TYPE_INTR, 0);
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (NDI_SUCCESS);
}
#ifdef BUSRA_DEBUG
void
{
struct ra_type_map *typemap;
struct ra_dip_type *dipmap;
struct ra_resource *res;
continue;
}
continue;
}
}
break;
}
break;
}
}
#endif
struct bus_range { /* 1275 "bus-range" property definition */
struct busnum_ctrl {
int rv;
};
/*
* Setup resource map for the pci bus node based on the "available"
* property and "bus-range" property.
*/
int
{
int len;
struct busnum_ctrl ctrl;
int circular_count;
int rval = NDI_SUCCESS;
/*
* If this is a pci bus node then look for "available" property
* to find the available resources on this bus.
*/
return (NDI_FAILURE);
/* it is not a pci bus type */
return (NDI_FAILURE);
/* read the "available" property if it is available */
return (NDI_FAILURE);
/*
* The pci-hotplug project addresses adding the call
* to pci_resource_setup from pci nexus driver.
* However that project would initially be only for x86,
* so for sparc pcmcia-pci support we still need to call
* pci_resource_setup in pcic driver. Once all pci nexus drivers
* are updated to call pci_resource_setup this portion of the
* code would really become an assert to make sure this
* function is not called for the same dip twice.
*/
{
return (NDI_FAILURE);
}
}
return (NDI_FAILURE);
}
return (NDI_FAILURE);
}
return (NDI_FAILURE);
}
NDI_FAILURE) {
return (NDI_FAILURE);
}
/* create the available resource list for both memory and io space */
for (i = 0; i < rcount; i++) {
case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
(void) ndi_ra_free(dip,
0);
break;
case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
break;
default:
"pci_resource_setup: bad addr type: %x\n",
break;
}
}
/*
* Create resource map for available bus numbers if the node
* has available-bus-range or bus-range property.
*/
DDI_SUCCESS) {
/*
* Add bus numbers in the range to the free list.
*/
1, NDI_RA_TYPE_PCI_BUSNUM, 0);
} else {
/*
* We don't have an available-bus-range property. If, instead,
* we have a bus-range property we add all the bus numbers
* in that range to the free list but we must then scan
* for pci-pci bridges on this bus to find out the if there
* are any of those bus numbers already in use. If so, we can
* reclaim them.
*/
&len) == DDI_SUCCESS) {
/*
* Add bus numbers other than the secondary
* bus number to the free list.
*/
(void) ndi_ra_free(dip,
/* scan for pci-pci bridges */
claim_pci_busnum, (void *)&ctrl);
/* failed to create the map */
(void) ndi_ra_map_destroy(dip,
rval = NDI_FAILURE;
}
}
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (rval);
}
/*
* If the device is a PCI bus device (i.e bus-range property exists) then
* claim the bus numbers used by the device from the specified bus
* resource map.
*/
static int
{
struct bus_range pci_bus_range;
struct busnum_ctrl *ctrl;
int len;
/* check if this is a PCI bus node */
return (DDI_WALK_PRUNECHILD);
/* it is not a pci bus type */
return (DDI_WALK_PRUNECHILD);
/* look for the bus-range property */
/* claim the bus range from the bus resource map */
NDI_RA_TYPE_PCI_BUSNUM, 0) == NDI_SUCCESS)
return (DDI_WALK_PRUNECHILD);
}
}
/*
* Error return.
*/
return (DDI_WALK_TERMINATE);
}
void
{
}
int
{
int i;
return (NDI_FAILURE);
return (NDI_FAILURE);
return (NDI_FAILURE);
/* for each entry in the PCI "available" property */
goto err;
case PCI_REG_ADDR_G(PCI_ADDR_MEM32): {
(void) ndi_ra_free(dip,
(avail_p->pci_phys_hi &
PCI_REG_PF_M) ?
0);
}
break;
case PCI_REG_ADDR_G(PCI_ADDR_IO):
(void) ndi_ra_free(dip,
0);
break;
default:
goto err;
}
}
#ifdef BUSRA_DEBUG
if (busra_debug) {
}
#endif
return (NDI_SUCCESS);
err:
i, avail_p->pci_phys_hi);
return (NDI_FAILURE);
}