ebus.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"
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/autoconf.h>
#include <sys/sysmacros.h>
#ifdef DEBUG
uint64_t ebus_debug_flags = 0;
#endif
/*
* The values of the following variables are used to initialize
* the cache line size and latency timer registers in the ebus
* configuration header. Variables are used instead of constants
*/
/*
* function prototypes for bus ops routines:
*/
static int
static int
static int
/*
* function prototypes for dev ops routines:
*/
/*
* general function prototypes:
*/
struct cb_ops ebus_cb_ops = {
ebus_open, /* open */
ebus_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
ebus_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
NULL, /* streamtab */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
/*
* bus ops and dev ops structures:
*/
static struct bus_ops ebus_bus_ops = {
NULL,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
0,
0,
};
0,
};
/*
* module definitions:
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"ebus nexus driver %I%", /* Name of module. */
&ebus_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
/*
* driver global data:
*/
static void *per_ebus_state; /* per-ebus soft state pointer */
int
_init(void)
{
int e;
/*
* Initialize per-ebus soft state pointer.
*/
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 */
/*ARGSUSED*/
static int
{
int instance;
switch (infocmd) {
default:
return (DDI_FAILURE);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
}
/*
* attach entry point:
*
* normal attach:
*
* create soft state structure (dip, reg, nreg and state fields)
* map in configuration header
* make sure device is properly configured
* report device
*/
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate soft state for this instance.
*/
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Set ebus type field based on ddi name info */
} else {
}
/* Get our ranges property for mapping child registers. */
return (DDI_FAILURE);
}
/*
* create minor node for devctl interfaces
*/
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Make sure the master enable and memory access enable
* bits are set in the config command register.
*/
if (!ebus_config(ebus_p)) {
return (DDI_FAILURE);
}
}
/*
* Make the pci_report_pmcap() call only for RIO
* implementations.
*/
(void *)EBUS_4MHZ);
}
/*
* Make the state as attached and report the device.
*/
return (DDI_SUCCESS);
case DDI_RESUME:
/*
* Make sure the master enable and memory access enable
* bits are set in the config command register.
*/
if (!ebus_config(ebus_p)) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* detach entry point:
*/
static int
{
switch (cmd) {
case DDI_DETACH:
case EBUS_TYPE:
sizeof (struct ebus_pci_rangespec));
break;
case FEBUS_TYPE:
sizeof (struct febus_rangespec));
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
int
{
struct ebus_pci_rangespec *rangep;
struct febus_rangespec *ferangep;
case EBUS_TYPE:
&range_len) != DDI_SUCCESS) {
return (DDI_ME_REGSPEC_RANGE);
}
if (nrange == 0) {
return (DDI_FAILURE);
}
#ifdef DEBUG
{
int i;
for (i = 0; i < nrange; i++) {
"ebus range addr 0x%x.0x%x PCI range "
"addr 0x%x.0x%x.0x%x ",
rangep[i].ebus_phys_hi,
rangep[i].ebus_phys_low,
rangep[i].pci_phys_hi,
rangep[i].pci_phys_mid,
rangep[i].pci_phys_low);
}
}
#endif /* DEBUG */
return (DDI_SUCCESS);
case FEBUS_TYPE:
DDI_PROP_DONTPASS, "ranges",
return (DDI_ME_REGSPEC_RANGE);
}
if (nrange == 0) {
return (DDI_FAILURE);
}
#ifdef DEBUG
{
int i;
for (i = 0; i < nrange; i++) {
"ebus range addr 0x%x.0x%x"
" Parent range "
"addr 0x%x.0x%x ",
ferangep[i].parent_phys_low);
}
}
#endif /* DEBUG */
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* bus driver entry points */
/*
* bus map entry point:
*
* if map request is for an rnumber
* get the corresponding regspec from device node
* build a new regspec in our parent's format
* build a new map_req with the new regspec
* call up the tree to complete the mapping
*/
static int
{
int rnumber, i, n;
int rval = DDI_SUCCESS;
/*
* Handle the mapping according to its type.
*/
case DDI_MT_REGSPEC:
/*
* We assume the register specification is in ebus format.
* We must convert it into a PCI format regspec and pass
* the request to our parent.
*/
mp->map_handlep);
break;
case DDI_MT_RNUMBER:
/*
* Get the "reg" property from the device node and convert
* it to our parent's format.
*/
return (DDI_ME_RNUMBER_RANGE);
}
n = i / sizeof (ebus_regspec_t);
return (DDI_ME_RNUMBER_RANGE);
}
break;
default:
return (DDI_ME_INVAL);
}
/* Adjust our reg property with offset and length */
if (len)
/*
* Now we have a copy the "reg" entry we're attempting to map.
* Translate this into our parents PCI address using the ranges
* property.
*/
case EBUS_TYPE:
break;
case FEBUS_TYPE:
break;
default:
rval = DDI_FAILURE;
}
if (rval != DDI_SUCCESS)
return (rval);
#ifdef DEBUG
case EBUS_TYPE:
break;
case FEBUS_TYPE:
break;
}
#endif
p_map_request = *mp;
case EBUS_TYPE:
break;
case FEBUS_TYPE:
break;
default:
return (DDI_FAILURE);
}
return (rval);
}
static int
{
int b;
int rval = DDI_SUCCESS;
static char out_of_range[] =
"Out of range register specification from device node <%s>";
/* Check for the correct space */
/* See if we fit in this range */
rangep->ebus_phys_low) &&
<= (rangep->ebus_phys_low +
/*
* Use the range entry to translate
* the EBUS physical address into the
* parents PCI space.
*/
rp->pci_phys_hi =
rp->pci_phys_low =
rp->pci_size_hi = 0;
rp->pci_size_low =
addr_offset));
"mid0x%x lo0x%x size 0x%x\n",
break;
}
}
if (b == nrange) {
return (DDI_ME_REGSPEC_RANGE);
}
return (rval);
}
static int
int b;
int rval = DDI_SUCCESS;
static char out_of_range[] =
"Out of range register specification from device node <%s>";
/* Check for the correct space */
/* See if we fit in this range */
rangep->febus_phys_low) &&
<= (rangep->febus_phys_low +
/*
* Use the range entry to translate
* the FEBUS physical address into the
* parents space.
*/
rp->regspec_addr =
rp->regspec_size =
addr_offset));
"lo0x%x size 0x%x\n",
break;
}
}
if (b == nrange) {
return (DDI_ME_REGSPEC_RANGE);
}
return (rval);
}
static int
{
int reglen;
/*
* Get the address portion of the node name based on the
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* control ops entry point:
*
* Requests handled completely:
* DDI_CTLOPS_INITCHILD
* DDI_CTLOPS_UNINITCHILD
* DDI_CTLOPS_REPORTDEV
* DDI_CTLOPS_REGSIZE
* DDI_CTLOPS_NREGS
*
* All others passed to parent.
*/
static int
{
#ifdef DEBUG
#endif
int i, n;
char name[10];
switch (op) {
case DDI_CTLOPS_INITCHILD: {
/*
* Set the address portion of the node name based on the
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
return (DDI_FAILURE);
}
n = i / sizeof (ebus_regspec_t);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_CTLOPS_NREGS:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Now pass the request up to our parent.
*/
}
struct ebus_string_to_pil {
};
{"audio", 9},
{"fdthree", 8},
{"floppy", 8},
{"ecpp", 3},
{"parallel", 3},
{"su", 12},
{"se", 12},
{"serial", 12},
{"power", 14}};
{"block", 8}};
static int
{
#ifdef DEBUG
#endif
char *name_p, *device_type_p;
/*
* NOTE: These ops below will never be supported in this nexus
* driver, hence they always return immediately.
*/
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = 0;
return (DDI_SUCCESS);
case DDI_INTROP_SETCAP:
case DDI_INTROP_SETMASK:
case DDI_INTROP_CLRMASK:
case DDI_INTROP_GETPENDING:
return (DDI_ENOTSUP);
default:
break;
}
goto done;
/*
* This is a hack to set the PIL for the devices under ebus.
* We first look up a device by it's specific name, if we can't
* match the name, we try and match it's device_type property.
* Lastly we default a PIL level of 1.
*/
max_children = sizeof (ebus_name_to_pil) /
sizeof (struct ebus_string_to_pil);
for (i = 0; i < max_children; i++) {
ebus_name_to_pil[i].pil);
goto done;
}
}
max_device_types = sizeof (ebus_device_type_to_pil) /
sizeof (struct ebus_string_to_pil);
for (i = 0; i < max_device_types; i++) {
device_type_p) == 0) {
"PIL %d\n", ebus_device_type_to_pil[i].
break;
}
}
}
/*
* If we get here, we need to set a default value
* for the PIL.
*/
}
done:
/* Pass up the request to our parent. */
}
static int
{
/*
* Make sure the master enable and memory access enable
* bits are set in the config command register.
*/
return (0);
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
return (1);
}
#ifdef DEBUG
extern void prom_printf(const char *, ...);
static void
{
char *s;
if (ebus_debug_flags & flag) {
switch (flag) {
case D_ATTACH:
s = "attach"; break;
case D_DETACH:
s = "detach"; break;
case D_MAP:
s = "map"; break;
case D_CTLOPS:
s = "ctlops"; break;
case D_INTR:
s = "intr"; break;
}
if (ebus_p)
else
}
}
#endif
/* ARGSUSED3 */
static int
{
/*
* Make sure the open is for the right file type.
*/
return (EINVAL);
/*
* Get the soft state structure for the device.
*/
return (ENXIO);
/*
* Handle the open by tracking the device state.
*/
return (EBUSY);
}
} else {
return (EBUSY);
}
}
return (0);
}
/* ARGSUSED */
static int
{
return (EINVAL);
return (ENXIO);
return (0);
}
/*
* ebus_ioctl: devctl hotplug controls
*/
/* ARGSUSED */
static int
int *rvalp)
{
struct devctl_iocdata *dcp;
int rv = 0;
return (ENXIO);
/*
* We can use the generic implementation for these ioctls
*/
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_BUS_GETSTATE:
}
/*
* read devctl ioctl data
*/
return (EFAULT);
switch (cmd) {
case DEVCTL_DEVICE_RESET:
break;
case DEVCTL_BUS_QUIESCE:
if (bus_state == BUS_QUIESCED)
break;
break;
case DEVCTL_BUS_UNQUIESCE:
if (bus_state == BUS_ACTIVE)
break;
break;
case DEVCTL_BUS_RESET:
break;
case DEVCTL_BUS_RESETALL:
break;
default:
}
return (rv);
}