/*
* 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/ddi_subrdefs.h>
#include <sys/autoconf.h>
#include <sys/sysmacros.h>
#ifdef DEBUG
#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:
*/
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:
*/
NULL,
NULL,
NULL,
NULL,
0,
0,
0,
0,
0,
0,
0,
0,
};
0,
NULL,
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* module definitions:
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"ebus nexus driver", /* Name of module. */
&ebus_ops, /* driver ops */
};
};
/*
* driver global data:
*/
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) {
case DDI_INFO_DEVT2INSTANCE:
break;
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
break;
default:
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);
}
/* Get our ranges property for mapping child registers. */
goto attach_fail;
}
/*
* create minor node for devctl interfaces
*/
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
goto attach_fail;
}
goto attach_fail;
}
/*
* Make the pci_report_pmcap() call only for RIO
* implementations.
*/
(void *)EBUS_4MHZ);
}
/*
* Make the state as attached and report the device.
*/
break;
case DDI_RESUME:
(void) ebus_config(ebus_p);
break;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* detach entry point:
*/
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
break;
default:
"failed to recognize ebus detach command\n");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
{
!= DDI_SUCCESS) {
return (DDI_ME_REGSPEC_RANGE);
}
if (ebus_p->vrange_cnt == 0) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
/* XXX rootnex assumes 1 cell and does not respect #size-cells */
if (ddi_root_node() == pdip)
}
/* 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;
/*
* 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)
if (rval != DDI_SUCCESS)
return (rval);
p_map_request = *mp;
return (rval);
}
/*
* ebus_apply_range generically relocates child's regspec to
* parent's format according to ebus' range spec
*
* Assumptions:
* - rng_caddr_hi is the space type
* - rng_caddr_low is the base address
* - ebus address is 32 bit and ebus entirely lives between 0-4G of
* parent space, so maths on preg/rng_cell_p[ebus_p->ebus_paddr_cells - 1],
* preg_cell_p[i], rng_caddr_low and ebus_rp->size are sufficient.
*/
static int
int b, i;
static char out_of_range[] =
"Out of range register specification from device node <%s>";
rng_caddr_hi = rng_cell_p[0];
/* Check for correct space */
continue;
/* Detect whether request entirely fits within a range */
if (req_addr < rng_caddr_low)
continue;
continue;
/* parent addr = child addr + offset from ranges */
for (i = 0; i < preg_rec_sz; i++)
preg_cell_p[i] = 0;
/* Copy the physical address */
for (i = 0; i < ebus_p->ebus_paddr_cells; i++)
/* Copy the size */
rng_sz - addr_offset);
#ifdef DEBUG
#endif /* DEBUG */
break;
}
if (b == nrange) {
return (DDI_ME_REGSPEC_RANGE);
}
return (DDI_SUCCESS);
}
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;
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
/*
* 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 = DDI_INTR_FLAG_LEVEL;
return (DDI_SUCCESS);
DDI_INTR_TYPE_FIXED : 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. */
}
/*
* ebus_config: setup pci config space registers:
* enable bus mastering, memory access and error reporting
*/
static int
{
char *devtype_str;
int devtype_len;
&devtype_len) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (comm)
return (DDI_SUCCESS);
/*
* Make sure the master enable and memory access enable
* bits are set in the config command register.
*/
return (DDI_FAILURE);
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
return (DDI_SUCCESS);
}
#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
}
}
static void
{
}
}
#endif /* DEBUG */
/* 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)
{
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);
}