niumx.c revision e778ae4497a8de30b58b85bd187bf4ef4ba8cd47
/*
* 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.
*/
/*
* Niagara2 Network Interface Unit (NIU) Nexus Driver
*/
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/machsystm.h>
#include <sys/hypervisor_api.h>
#include "niumx_var.h"
static struct bus_ops niumx_bus_ops = {
0,
0,
0,
0,
0,
0,
0,
0, /* (*bus_get_eventcookie)(); */
0, /* (*bus_add_eventcall)(); */
0, /* (*bus_remove_eventcall)(); */
0, /* (*bus_post_event)(); */
0, /* (*bus_intr_ctl)(); */
0, /* (*bus_config)(); */
0, /* (*bus_unconfig)(); */
niumx_fm_init_child, /* (*bus_fm_init)(); */
0, /* (*bus_fm_fini)(); */
0, /* (*bus_enter)() */
0, /* (*bus_exit)() */
0, /* (*bus_power)() */
niumx_intr_ops /* (*bus_intr_op)(); */
};
DEVO_REV, /* devo_rev */
0, /* refcnt */
ddi_no_info, /* info */
nulldev, /* identify */
0, /* probe */
niumx_attach, /* attach */
niumx_detach, /* detach */
nulldev, /* reset */
(struct cb_ops *)0, /* driver operations */
&niumx_bus_ops, /* bus operations */
0, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/* Module linkage information for the kernel. */
&mod_driverops, /* Type of module */
"NIU Nexus Driver",
&niumx_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
static void *niumx_state;
/*
* forward function declarations:
*/
static void niumx_removechild(dev_info_t *);
int
_init(void)
{
int e;
/*
* Check HV intr group api versioning.
* This driver uses the old interrupt routines which are supported
* in old firmware in the CORE API group and in newer firmware in
* the INTR API group. Support for these calls will be dropped
* once the INTR API group major goes to 2.
*/
(mjrnum > NIUMX_INTR_MAJOR_VER)) {
return (ENOTSUP);
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) == 0)
return (e);
}
int
{
}
void
niumx_intr_dist(void *arg)
{
int i;
for (i = 0; i < NIUMX_MAX_INTRS; i++, ih_p++) {
int intr_state, state;
if (!sysino || /* sequence is significant */
(intr_state == HV_INTR_NOTVALID) ||
continue;
/* check for pending interrupts, busy wait if so */
"pending interrupt (%x,%lx) timedout\n",
(void) hvio_intr_setstate(sysino,
break;
}
}
}
}
static int
{
int ret = DDI_SUCCESS;
switch (cmd) {
case DDI_ATTACH:
!= DDI_PROP_SUCCESS) {
ret = DDI_FAILURE;
goto done;
}
/*
* Allocate and get soft state structure.
*/
!= DDI_SUCCESS) {
ret = DDI_FAILURE;
goto prop_free;
}
instance);
/* hv devhdl: low 28-bit of 1st "reg" entry's addr.hi */
/* add interrupt redistribution callback */
ret = DDI_SUCCESS;
goto prop_free;
done:
return (ret);
case DDI_RESUME:
default:
break;
}
return (ret);
}
static int
{
switch (cmd) {
case DDI_DETACH:
niumxds_p = (niumx_devstate_t *)
return (DDI_SUCCESS);
case DDI_SUSPEND:
default:
break;
}
return (DDI_FAILURE);
}
/*
* Function used to initialize FMA for our children nodes. Called
* through pci busops when child node calls ddi_fm_init.
*/
/*ARGSUSED*/
int
{
return (niumxds_p->niumx_fm_cap);
}
/*ARGSUSED*/
int
{
return (DDI_FAILURE);
return (DDI_ME_RNUMBER_RANGE);
}
/* build regspec up for parent */
return (DDI_FAILURE);
}
/* locate matching ranges record */
break;
}
if (i >= rngnum) {
goto err;
}
/*
* validate request has matching bus type and within 4G
* limit by comparing addr.hi of "ranges" and child "reg".
*/
/* check to verify reg bounds are within rng bounds */
goto err;
}
err:
return (ret);
}
/*
* niumx_ctlops
*/
int
{
if (rdip == (dev_info_t *)0)
return (DDI_FAILURE);
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
/* fall through */
break;
default:
}
*(int *)result = 0;
return (DDI_FAILURE);
if (ctlop == DDI_CTLOPS_NREGS) {
totreg);
} else if (ctlop == DDI_CTLOPS_REGSIZE) {
int rn;
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* niumx_name_child
*
* This function is called from init_child to name a node. It is
* also passed as a callback for node merging functions.
*
* return value: DDI_SUCCESS, DDI_FAILURE
*/
static int
{
niu_regspec_t *r;
uint_t n;
if (ndi_dev_is_persistent_node(child) == 0) {
char **unit_addr;
/* name .conf nodes by "unit-address" property */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* name hardware nodes by "reg" property */
"reg", (int **)&r, &n) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ddi_prop_free(r);
return (DDI_SUCCESS);
}
static int
{
char name[MAXNAMELEN];
/*
* Non-peristent nodes indicate a prototype node with per-instance
* properties to be merged into the real h/w device node.
*/
if (ndi_dev_is_persistent_node(child) == 0) {
niu_regspec_t *r;
uint_t n;
DDI_PROP_DONTPASS, "reg", (int **)&r, &n) ==
DDI_SUCCESS) {
"cannot merge prototype from %s.conf",
ddi_prop_free(r);
return (DDI_NOT_WELL_FORMED);
}
return (DDI_NOT_WELL_FORMED);
/*
* Try to merge the properties from this prototype
* node into real h/w nodes.
*/
/*
* Merged ok - return failure to remove the node.
*/
return (DDI_FAILURE);
}
/*
* The child was not merged into a h/w node,
* but there's not much we can do with it other
* than return failure to cause the node to be removed.
*/
return (DDI_NOT_WELL_FORMED);
}
/*
* Initialize real h/w nodes
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static void
{
}
/*
* bus dma alloc handle entry point:
*/
/*ARGSUSED*/
int
{
return (DDI_DMA_BADATTR);
}
/* Caution: we don't use zalloc to enhance performance! */
return (DDI_FAILURE);
}
mp->dmai_fault = 0;
return (DDI_SUCCESS);
}
/*
* bus dma free handle entry point:
*/
/*ARGSUSED*/
int
{
if (mp->dmai_cookie)
return (DDI_SUCCESS);
}
/*
* bus dma bind handle entry point:
*
* of this dma request.
* Note: this only works with DMA_OTYP_VADDR, and makes use of the known
* fact that only contiguous memory blocks will be passed in.
* Therefore only one cookie will ever be returned.
*
* return values:
* DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type
* DDI_DMA_NORESOURCES
* DDI_SUCCESS
*
* dma handle members affected (set on exit):
* mp->dmai_object - dmareq->dmar_object
* mp->dmai_rflags - dmareq->dmar_flags
* mp->dmai_roffset - initialized to starting page offset
* mp->dmai_size - # of total pages of entire object
* mp->dmai_cookie - new cookie alloc'd
*/
/*ARGSUSED*/
int
{
int ret;
/* first check dma type */
case DMA_OTYP_VADDR: {
}
break;
case DMA_OTYP_BUFVADDR:
case DMA_OTYP_PAGES:
case DMA_OTYP_PADDR:
default:
goto err;
}
if (pfn0 == PFN_INVALID) {
goto err;
}
goto err;
}
*ccountp = 1;
return (DDI_DMA_MAPPED);
err:
"niumx_dma_bindhdl error ret=%d\n", ret);
return (ret);
}
/*
* bus dma unbind handle entry point:
*/
/*ARGSUSED*/
int
{
if (mp->dmai_cookie) {
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
int
{
int ret = DDI_SUCCESS;
switch (intr_op) {
*(int *)result = DDI_INTR_TYPE_FIXED;
break;
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_SETCAP:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_ALLOC:
/* scratch1 = count, # of intrs from DDI framework */
break;
case DDI_INTROP_FREE:
/* Do we need to do anything here? */
break;
case DDI_INTROP_GETPRI:
*(int *)result = NIUMX_DEFAULT_PIL;
break;
case DDI_INTROP_SETPRI:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_ADDISR:
break;
case DDI_INTROP_REMISR:
break;
case DDI_INTROP_ENABLE:
break;
case DDI_INTROP_DISABLE:
break;
case DDI_INTROP_SETMASK:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_CLRMASK:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_GETPENDING:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL: {
int inoslen;
!= DDI_SUCCESS) {
ret = DDI_FAILURE;
break;
}
}
break;
default:
ret = DDI_ENOTSUP;
break;
}
return (ret);
}
int
{
/* find the appropriate slot from the fixed table */
ret = DDI_FAILURE;
goto fail;
}
if (valid == HV_INTR_VALID)
!= H_EOK) {
hvret);
ret = DDI_FAILURE;
}
fail:
return (ret);
}
/*
* niumx_add_intr:
*
* This function is called to register interrupts.
*/
int
{
/* get new ino */
ret = DDI_FAILURE;
goto done;
}
ret = DDI_FAILURE;
goto done;
}
"ret 0x%x\n", hvret);
ret = DDI_FAILURE;
goto done;
}
/* Save sysino value in hdlp */
/* swap in our handler & arg */
/* Restore orig. interrupt handler & args in handle. */
if (ret != DDI_SUCCESS) {
goto done;
}
/* select cpu, saving it for removal */
!= H_EOK) {
hvret);
ret = DDI_FAILURE;
}
done:
return (ret);
}
/*
* niumx_rem_intr:
*
* This function is called to unregister interrupts.
*/
int
{
/* find the appropriate slot from the fixed table */
ret = DDI_FAILURE;
goto fail1;
}
/* check for pending interrupts, busy wait if so */
"pending interrupt (%x,%lx) timedout\n",
ret = DDI_FAILURE;
goto fail2;
}
}
return (ret);
}
/*
* niumx_intr_hdlr (our interrupt handler)
*/
niumx_intr_hdlr(void *arg)
{
uint_t r;
return (r);
}
#ifdef DEBUG
static char *niumx_debug_sym [] = { /* same sequence as niumx_debug_bit */
/* 0 */ "attach",
/* 1 */ "map",
/* 2 */ "nex-ctlops",
/* 3 */ "introps",
/* 4 */ "intr-add",
/* 5 */ "intr-rem",
/* 6 */ "intr",
/* 7 */ "dma-alloc",
/* 8 */ "dma-bind",
/* 9 */ "dma-unbind",
/* 10 */ "chk-dma-mode"
};
/*ARGSUSED*/
void
{
char msgbuf[1024];
return;
}
#endif /* DEBUG */