/*
* 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:
*/
#ifdef ACEBUS_HOTPLUG
#endif
/*
* 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_ctl)(); */
NULL, /* (*bus_config)(); */
NULL, /* (*bus_unconfig)(); */
NULL, /* (*bus_fm_init)(); */
NULL, /* (*bus_fm_fini)(); */
NULL, /* (*bus_fm_access_enter)(); */
NULL, /* (*bus_fm_access_fini)(); */
NULL, /* (*bus_power)(); */
acebus_intr_ops /* (*bus_intr_op)(); */
};
0,
(struct cb_ops *)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 */
"Alarm Card ebus nexus", /* Name of module. */
&acebus_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 */
/*
* 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);
}
/*
* Make sure the master enable and memory access enable
* bits are set in the config command register.
*/
if (!acebus_config(ebus_p)) {
return (DDI_FAILURE);
}
/* Get our ranges property for mapping child registers. */
return (DDI_FAILURE);
}
/*
* 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 (!acebus_config(ebus_p)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* detach entry point:
*/
static int
{
switch (cmd) {
case DDI_DETACH:
sizeof (struct ebus_pci_rangespec));
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
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 "
}
}
#endif /* DEBUG */
return (DDI_SUCCESS);
}
/* 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)
/*
* Now we have a copy the "reg" entry we're attempting to map.
* Translate this into our parents PCI address using the ranges
* property.
*/
if (rval != DDI_SUCCESS)
return (rval);
#ifdef ACEBUS_HOTPLUG
/*
* The map operation provides a translated (not a re-assigned, or
* relocated) ebus address for the child in its address space(range).
* Ebus address space is relocatible but its child address space
* is not. As specified by their 'reg' properties, they reside
* at a fixed offset in their parent's (ebus's) space.
*
* By setting this bit, we will not run into HostPCI nexus
* trying to relocate a translated ebus address (which is already
* relocated) and failing the operation.
* The reason for doing this here is that the PCI hotplug configurator
* always marks the ebus space as relocatible (unlike OBP) and that
* information is implied for the child too, which is wrong.
*/
#endif
#ifdef DEBUG
#endif
p_map_request = *mp;
return (rval);
}
static int
{
int b;
static const 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);
}
/*
* 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 {
};
{"fdthree", 8},
{"ecpp", 3},
{"su", 12},
{"se", 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 (acebus_name_to_pil) /
sizeof (struct ebus_string_to_pil);
for (i = 0; i < max_children; i++) {
acebus_name_to_pil[i].pil);
goto done;
}
}
max_device_types = sizeof (acebus_device_type_to_pil) /
sizeof (struct ebus_string_to_pil);
for (i = 0; i < max_device_types; i++) {
device_type) == 0) {
"Device type %s; match PIL %d\n",
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
{
#ifdef ACEBUS_HOTPLUG
int tcr_reg;
};
#endif
/*
* 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
#ifdef ACEBUS_HOTPLUG
return (0);
}
&csr_handle) != DDI_SUCCESS) {
}
#ifdef DEBUG
if (acebus_debug_flags) {
TCR1_OFF)),
TCR2_OFF)),
TCR3_OFF)));
PMD_AUX_OFF)),
FREQ_AUX_OFF)));
#ifdef ACEBUS_DEBUG
#endif
} /* acebus_debug_flags */
#endif
/* If TCR registers are not initialized, initialize them here */
TCR1_OFF));
TCR2_OFF));
TCR3_OFF));
#ifdef DEBUG
if (acebus_debug_flags) {
TCR1_OFF)),
TCR2_OFF)),
TCR3_OFF)));
}
#endif
#endif /* ACEBUS_HOTPLUG */
return (1); /* return success */
}
#ifdef DEBUG
extern void prom_printf(const char *, ...);
static void
{
char *s;
if (acebus_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
#ifdef ACEBUS_HOTPLUG
static int
{
/*
* If "ranges" property is found, then the device is initialized
* by OBP, hence simply return.
* Otherwise we create all the properties here.
*/
return (DDI_SUCCESS);
}
/*
* interrupt-map is the only property that comes from a .conf file.
* Since it doesn't have the nodeid field set, it must be done here.
* Other properties can come from OBP or created here.
*/
return (DDI_FAILURE);
}
/*
* Create the "ranges" property.
* Ebus has BAR0 and BAR1 allocated (both in memory space).
* Other BARs are 0.
* Hence there are 2 memory ranges it operates in. (one for each BAR).
* ie. there are 2 entries in its ranges property.
*/
DDI_PROP_DONTPASS, "assigned-addresses",
return (DDI_FAILURE);
}
/*
* Create the 1st mem range in which it operates corresponding
* to BAR0
*/
for (i = 0; i < rnums; i++) {
found = 1;
break;
}
}
if (!found) {
return (DDI_FAILURE);
}
found = 0;
er[0].ebus_phys_low = 0;
/*
* Create the 2nd mem range in which it operates corresponding
* to BAR1
*/
for (i = 0; i < rnums; i++) {
found = 1;
break;
}
}
if (!found) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* The following properties are as defined by PCI 1275 bindings. */
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_FAILURE);
imask[0] = 0x1f;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* This function takes in the ac-interrupt-map property from the .conf file,
* fills in the 'nodeid' information and then creates the 'interrupt-map'
* property.
*/
static int
{
int ilength;
/*
* interrupt-map is specified via .conf file in hotplug mode,
* since the child configuration is static.
* It could even be hardcoded in the driver.
*/
/* assume default implementation */
acebus_default_se_imap[0] = 0x14;
acebus_default_se_imap[3] = 0;
ilength = 5;
default_ival = 1;
}
for (i = 0; i < num; i++) {
imap_ok = 0;
}
if (imap_ok) {
if (!default_ival)
return (DDI_SUCCESS);
}
if (!default_ival)
return (DDI_FAILURE);
}
}
for (i = 0; i < num; i++) {
}
if (!default_ival)
return (DDI_FAILURE);
}
if (!default_ival)
return (DDI_SUCCESS);
}
#endif /* ACEBUS_HOTPLUG */