upa64s.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"
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
/*
* driver global data:
*/
static void *per_upa64s_state; /* soft state pointer */
/*
* function prototypes for bus ops routines:
*/
static int
static int
static int
static int
static int
/*
* function prototypes for dev ops routines:
*/
/*
* bus ops and dev ops structures:
*/
static struct bus_ops upa64s_bus_ops = {
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
static struct dev_ops upa64s_ops = {
0,
0,
(struct cb_ops *)0,
};
/*
* module definitions:
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* type of module */
"UPA64S nexus driver %I%", /* name of module */
&upa64s_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int e;
/*
* Initialize per instance bus soft state pointer.
*/
if (e = ddi_soft_state_init(&per_upa64s_state,
sizeof (upa64s_devstate_t), 2))
return (e);
/*
* Install the module.
*/
if (e = mod_install(&modlinkage))
return (e);
}
int
_fini(void)
{
int e = mod_remove(&modlinkage);
if (e)
return (e);
return (e);
}
int
{
}
/*
* forward declarations:
*/
static void upa64s_intrdist(void *arg);
/* device driver entry points */
/*
* attach entry point:
*/
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
/*
* Allocate and get the per instance soft state structure.
*/
return (DDI_FAILURE);
}
/*
* Get key properties of the bridge node.
*/
goto fail;
/*
* Create "pm-components" property for the purpose of
* doing Power Management.
*/
!= DDI_PROP_SUCCESS) {
goto fail;
}
/* Map in the UPA's registers */
if (ddi_regs_map_setup(dip, 0,
goto fail;
}
goto fail1;
}
goto fail2;
}
/*
* Power level of a component is unknown at attach time.
* Bring the power level to what is needed for normal operation.
*/
DDI_SUCCESS) {
goto fail3;
}
return (DDI_SUCCESS);
case DDI_RESUME:
/*
* Power level of a component is unknown at resume time.
* Bring the power level to what it was before suspend.
*/
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
fail:
return (DDI_FAILURE);
}
/*
* detach entry point:
*/
static int
{
switch (cmd) {
case DDI_DETACH:
/*
* Power down the device.
*/
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* power entry point:
*
* This entry point is called by Power Management framework to
* Schizo chip.
*/
static int
{
volatile uint64_t uint64_data;
if (component != UPA64S_PM_COMP ||
return (DDI_FAILURE);
/*
* We can't set the hardware to the state that it is
* already in. So if the power state is not known, inquire the
* state of the hardware. If it is already in that state,
* record and return, otherwise make the state change.
*/
if ((level == UPA64S_PM_RESET &&
uint64_data == UPA64S_NOT_POK_RST_L) ||
(level == UPA64S_PM_NORMOP &&
uint64_data == UPA64S_POK_NOT_RST_L)) {
return (DDI_SUCCESS);
}
}
level);
return (DDI_SUCCESS);
}
if (level == UPA64S_PM_RESET) {
/*
* Assert UPA64S_RESET
*/
/*
* Deassert UPA64S_POK. Flush the store buffer.
*/
/*
* Internal UPA clock to 1/2 speed
*/
/*
* Internal UPA clock to 1/64 speed. Flush the store buffer.
*/
} else {
/*
* Internal UPA clock to 1/2 speed
*/
/*
* Internal UPA clock to full speed. Flush the store buffer.
*/
/*
* Assert UPA64S_POK. Flush the store buffer before
* the wait delay.
*/
/*
* Delay 20 milliseconds for the signals to settle down.
*/
/*
* Deassert UPA64S_RESET. Flush the store buffer.
*/
}
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
{
/*
* User level mappings are not supported yet.
*/
return (DDI_ME_UNIMPLEMENTED);
}
/*
* Now handle the mapping according to its type.
*/
case DDI_MT_REGSPEC:
/*
* We assume the register specification is in PCI format.
* We must convert it into a regspec of our parent's
* 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.
*/
if (rnumber < 0)
return (DDI_ME_RNUMBER_RANGE);
break;
default:
return (DDI_ME_INVAL);
}
if (rval != DDI_SUCCESS) {
return (rval);
}
/*
* Now we have a copy of the upa64s regspec converted to our parent's
* format. Build a new map request based on this regspec and pass
* it to our parent.
*/
p_map_request = *mp;
return (rval);
}
/*
* Translate the UPA devices interrupt property. This is the only case I
* know of where the interrupts property is meaningless. As a result, we
* just use UPA_BASE_INO as our interrupt value and add to it the upa port id.
* UPA portid is returned too.
*/
#define UPA_BASE_INO 0x2a
static int
{
/* Clear the ffb's interrupts property, it's meaningless */
"upa-portid", -1)) == -1)
return (-1);
return (portid);
}
/*
* bus add intrspec entry point:
*/
static int
{
#ifdef DEBUG
#endif /* DEBUG */
return (DDI_FAILURE);
"rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
/*
* Make sure an interrupt handler isn't already installed.
*/
return (DDI_FAILURE);
}
/*
* Install the handler in the system table.
*/
#ifdef DEBUG
#endif
return (DDI_FAILURE);
cpu_id = intr_dist_cpuid();
/*
* Enable the interrupt through its interrupt mapping register.
*/
/* Read the data back to flush store buffers. */
return (DDI_SUCCESS);
}
/*
* bus remove intrspec entry point
*/
static int
{
int upaport;
#ifndef lint
#endif
/*
* Make sure the mondo is valid.
*/
return (DDI_FAILURE);
"rdip=%s%d - IDDI_INTR_TYPE_NORMAL, mondo=%x\n",
return (DDI_FAILURE);
}
/* Call up to our parent to handle the removal */
#ifndef lint
/* Flush store buffers */
#endif
return (DDI_SUCCESS);
}
/* new intr_ops structure */
static int
{
int ret = DDI_SUCCESS;
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = 0;
break;
case DDI_INTROP_ALLOC:
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
/*
* We only have slave UPA devices so force the PIL to 5.
* this is done since all slave UPA devices have historically
* had their PILs set to 5. Only do it if the PIL is not
* being preset.
*/
break;
case DDI_INTROP_SETPRI:
break;
case DDI_INTROP_ADDISR:
break;
case DDI_INTROP_REMISR:
break;
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
break;
case DDI_INTROP_SETCAP:
case DDI_INTROP_SETMASK:
case DDI_INTROP_CLRMASK:
case DDI_INTROP_GETPENDING:
ret = DDI_ENOTSUP;
break;
/* only support fixed interrupts */
DDI_INTR_TYPE_FIXED : 0;
break;
default:
break;
}
return (ret);
}
#ifdef DEBUG
extern void prom_printf(const char *, ...);
#endif
/*
* control ops entry point:
*
* Requests handled completely:
* DDI_CTLOPS_INITCHILD see init_child() for details
* DDI_CTLOPS_UNINITCHILD
* DDI_CTLOPS_REPORTDEV see report_dev() for details
* DDI_CTLOPS_XLATE_INTRS nothing to do
* DDI_CTLOPS_REGSIZE
* DDI_CTLOPS_NREGS
* DDI_CTLOPS_NINTRS
*
* All others passed to parent.
*/
static int
{
switch (op) {
case DDI_CTLOPS_INITCHILD:
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
return (report_dev(rdip));
case DDI_CTLOPS_XLATE_INTRS:
return (DDI_FAILURE);
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
return (DDI_SUCCESS);
case DDI_CTLOPS_NINTRS:
return (DDI_FAILURE);
}
/*
* Now pass the request up to our parent.
*/
}
/* support routines */
/*
* get_properties
*
* This function is called from the attach routine to get the key
* properties of the upa64s node.
*
* used by: upa64s_attach()
*
* return value: none
*/
static int
{
int safari_id;
/*
* Get the device's safari id.
*/
"portid", -1);
if (safari_id == -1) {
}
return (DDI_SUCCESS);
}
/*
* save_state
*
* This routine saves a copy of the upa64s register state.
*
* used by: upa64s_detach() on a suspend operation
*/
static void
{
}
/*
* restore_state
*
* This routine restores a copy of the upa64s register state.
*
* used by: upa64s_attach() on a resume operation
*/
static void
{
#ifndef lint
#endif
#ifndef lint
/* Flush the store buffer */
#endif
}
/*
* get_reg_set
*
* This routine will get a upa64s format regspec for a given
* device node and register number.
*
* used by: upa64s_map()
*
* return value:
*
* DDI_SUCCESS - on success
* DDI_ME_INVAL - regspec is invalid
* DDI_ME_RNUMBER_RANGE - rnumber out of range
*/
static int
{
int i, n, rval;
/*
* Get child device "reg" property
*/
return (DDI_ME_RNUMBER_RANGE);
n = i / (int)sizeof (upa64s_regspec_t);
if (rnumber >= n) {
return (DDI_ME_RNUMBER_RANGE);
}
/*
* Convert each the upa64s format register specification to
* out parent format.
*/
return (rval);
}
/*
* xlate_reg_prop
*
* This routine converts a upa64s format regspec to a standard
* regspec containing the corresponding system address.
*
* used by: upa64s_map()
*
* return value:
*
* DDI_SUCCESS
* DDI_FAILURE - off + len is beyond device address range
* DDI_ME_INVAL - regspec is invalid
*/
static int
{
int n_ranges, ranges_len, i;
#if 0
/*
* both FFB and AFB have broken "reg" properties, all mapping
* requests are done through reg-0 with very long offsets.
* Hence this safety check is always violated.
*/
return (DDI_FAILURE);
}
#endif
/*
* current "struct regspec" only supports 32-bit sizes.
*/
panic("upa64s: reg size must be less than 4 Gb");
ranges_len = 0;
}
#if 0
/*
* again, this safety checking can not be performed.
* Hack by adding a pratical max child reg bank length.
*/
#else
#define UPA64S_MAX_CHILD_LEN 0xe000000
#endif
break;
}
}
if (ranges_len)
}
/*
* report_dev
*
* This function is called from our control ops routine on a
* DDI_CTLOPS_REPORTDEV request.
*/
static int
{
if (dip == (dev_info_t *)0)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* init_child
*
* This function is called from our control ops routine on a
* DDI_CTLOPS_INITCHILD request. It builds and sets the device's
* parent private data area.
*
* used by: upa64s_ctlops()
*
* return value: none
*/
static int
{
int i;
char addr[256];
"upa-portid", -1)) == -1)
return (DDI_FAILURE);
/*
* Set the address portion of the node name based on
* the function and device number.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* get_reg_set_size
*
* Given a dev info pointer to a child and a register number, this
* routine returns the size element of that reg set property.
*
* used by: upa64s_ctlops() - DDI_CTLOPS_REGSIZE
*
* return value: size of reg set on success, -1 on error
*/
static off_t
{
int i;
if (rnumber < 0)
return (-1);
/*
* Get the reg property for the device.
*/
return (-1);
if (rnumber >= (i / (int)sizeof (upa64s_regspec_t))) {
return (-1);
}
/* >4G reg size not supported */
return (size);
}
/*
* get_nreg_set
*
* Given a dev info pointer to a child, this routine returns the
* number of sets in its "reg" property.
*
* used by: upa64s_ctlops() - DDI_CTLOPS_NREGS
*
* return value: # of reg sets on success, zero on error
*/
static uint_t
{
int i, n;
/*
* Get the reg property for the device.
*/
return (0);
n = i / (int)sizeof (upa64s_regspec_t);
return (n);
}
/*
* upa64s_intrdist
*
* The following routine is the callback function for this nexus driver
* to support interrupt distribution on sun4u systems. When this
* function is called by the interrupt distribution framework, it will
* reprogram all the active the mondo registers.
*/
static void
upa64s_intrdist(void *arg)
{
continue;
cpuid = intr_dist_cpuid();
/* Check and re-program cpu target if necessary */
continue;
}
}
}
#ifdef DEBUG
static void
{
char *s = NULL;
cont = 1;
}
if (!(upa64s_debug_flags & flag))
return;
switch (flag) {
case D_ATTACH: s = "attach"; break;
case D_DETACH: s = "detach"; break;
case D_POWER: s = "power"; break;
case D_MAP: s = "map"; break;
case D_CTLOPS: s = "ctlops"; break;
case D_G_ISPEC: s = "get_intrspec"; break;
case D_A_ISPEC: s = "add_intrspec"; break;
case D_R_ISPEC: s = "remove_intrspec"; break;
case D_INIT_CLD: s = "init_child"; break;
case D_INTRDIST: s = "intrdist"; break;
}
if (s && cont == 0) {
ddi_get_instance(dip), s);
}
}
#endif