/*
* 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 2009 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/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/serengeti.h>
#include <sys/sgsbbc_mailbox.h>
#include <sys/sgevents.h>
#include <sys/sysevent.h>
#include <sys/ndi_impldefs.h>
#include <sys/sbd_ioctl.h>
/* Useful debugging Stuff */
#include <sys/nexusdebug.h>
/*
* module ssm.c
*
* This module is a nexus driver designed to support the ssm nexus driver
* and all children below it. This driver does not handle any of the
* DDI functions passed up to it by the ssm driver, but instead allows
* them to bubble up to the root node.
*/
/*
* Function prototypes
*/
extern int plat_max_boards();
static int
static int
static int
static int
static int
static int
static int
static int
static int
/*
* FMA error callback
* Register error handling callback with our parent. We will just call
* our children's error callbacks and return their status.
*/
static int
/*
* fm_init busop to initialize our children
*/
static int
/*
* register/unregister our callback.
*/
static void
static void
/*
* DR event handlers
* We want to register the event handlers once for all instances. In the
* other hand we have register them after the sbbc has been attached.
* event_initialize gives us the logic of only registering the events only
* once
*/
int event_initialized = 0;
uint_t ssm_dr_event_handler(char *);
/*
* Event lock and state
*/
int ssm_event_state;
/*
* DR event msg and payload
*/
struct ssm_node2inst {
int inst;
};
/*
* Configuration data structures
*/
ddi_bus_map, /* map */
0, /* get_intrspec */
0, /* add_intrspec */
0, /* remove_intrspec */
i_ddi_map_fault, /* map_fault */
0, /* dma_map */
ddi_dma_mctl, /* dma_ctl */
ssm_ctlops, /* ctl */
ddi_bus_prop_op, /* prop_op */
0,
0,
0,
NULL,
NULL,
NULL,
0,
};
ssm_open, /* open */
ssm_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
ssm_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)() */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
ssm_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
ssm_attach, /* attach */
ssm_detach, /* detach */
nulldev, /* reset */
&ssm_cb_ops, /* driver operations */
&ssm_bus_ops, /* bus_ops */
nulldev, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
/*
* Driver globals
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"SSM Nexus", /* name of module */
&ssm_ops, /* driver ops */
};
MODREV_1, /* rev */
(void *)&modldrv,
};
/*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
#if defined(DEBUG)
debug_print_level = 0x0;
#endif
/* Initialize soft state pointer. */
sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
return (error);
/* Install the module. */
if (error != 0)
return (error);
}
int
_fini(void)
{
int error;
/* Remove the module. */
return (error);
/*
* Unregister the event handler
*/
/* Free the soft state info. */
return (0);
}
int
{
}
/* device driver entry points */
/*
* info entry point:
*/
/* ARGSUSED */
static int
{
int instance;
if (infocmd == DDI_INFO_DEVT2INSTANCE) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* attach entry point:
*/
static int
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/* Set the dip in the soft state */
instance, "nodeid");
return (DDI_FAILURE);
}
#if DEBUG
DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
#endif
return (DDI_FAILURE);
}
if (event_initialized == 0) {
int rv;
/*
* Register DR event handler
*/
event_initialized = 1;
}
/*
* Preallocate to avoid sleeping with ssm_node2inst_lock held -
* low level interrupts use this mutex.
*/
break;
}
}
return (DDI_SUCCESS);
}
/*
* detach entry point:
*/
/*ARGSUSED*/
static int
{
int (*sbd_teardown_instance) (int, caddr_t);
"ssm_open bad instance number %d", instance);
return (ENXIO);
}
switch (cmd) {
case DDI_DETACH:
sbd_teardown_instance = (int (*) (int, caddr_t))
if (!sbd_teardown_instance) {
return (DDI_FAILURE);
}
if (rv != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Only the head of the list can persist if unused */
break;
}
if (sp != &ssm_node2inst_map) {
} else {
/*
* Invalidate the head element, but retain the rest
* of the list - "next" is still valid.
*/
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int portid = 0;
extern uint_t root_phys_addr_lo_mask;
name[0] = '\0';
if (sparc_pd_getnreg(child) == 0)
return (DDI_SUCCESS);
if (portid == -1) {
} else {
}
return (DDI_SUCCESS);
}
static int
{
if ((ndi_dev_is_persistent_node(child) == 0) &&
return (DDI_FAILURE);
}
(void) init_regspec_64(child);
return (DDI_SUCCESS);
}
/*
* Control ops entry point:
*
* Requests handled completely:
* DDI_CTLOPS_INITCHILD
* DDI_CTLOPS_UNINITCHILD
* DDI_CTLOPS_REPORTDEV
* All others are passed to the parent.
* The name of the ssm node is ssm@nodeid,0.
* ssm is the equivalent of rootnex.
*/
static int
void *result)
{
int rval;
switch (op) {
case DDI_CTLOPS_INITCHILD: {
}
case DDI_CTLOPS_UNINITCHILD: {
return (DDI_SUCCESS);
}
case DDI_CTLOPS_REPORTDEV: {
char *p = buf;
int portid;
p += strlen(p);
/* Fetch Safari Extended Agent ID of this device. */
/*
* If this is one of the ssm children it will have
* portid property and its parent will be ssm.
* In this case report Node number and Safari id.
*/
if (portid != -1 &&
int node;
int safid;
int n;
n = sparc_pd_getnreg(rdip);
ASSERT(n > 0);
(void) strcpy(p, ": ");
p += strlen(p);
(void) sprintf(p, "Node %d Safari id %d 0x%x%s",
(n > 1 ? "" : " ..."));
p += strlen(p);
}
rval = DDI_SUCCESS;
break;
}
default:
break;
}
return (rval);
}
/*ARGSUSED*/
static int
{
int rv;
if (SG_BOARD_IS_CPU_TYPE(bd))
else
if (rv == DDI_FAILURE) {
"ssm_make_nodes:%d: failed to create "
"minor node (%s, 0x%x)",
return (-1);
}
}
return (0);
}
/* ARGSUSED */
static int
{
int rv;
return (ENXIO);
}
return (ENXIO);
}
return (EIO);
}
}
return (EIO);
}
sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
if (!sbd_setup_instance) {
return (EIO);
}
if (rv != DDI_SUCCESS) {
return (EIO);
}
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
return (ENXIO);
return (ENXIO);
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
int *rvalp)
{
char *addr;
return (ENXIO);
switch (cmd) {
case DEVCTL_BUS_CONFIGURE:
/*
* read devctl ioctl data
*/
return (EFAULT);
"DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
break;
case DEVCTL_BUS_UNCONFIGURE:
return (EFAULT);
"DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
break;
#ifdef DEBUG
case SSM_TEARDOWN_SBD: {
int (*sbd_teardown_instance) (int, caddr_t);
sbd_teardown_instance = (int (*) (int, caddr_t))
if (!sbd_teardown_instance) {
return (EFAULT);
}
if (rv != DDI_SUCCESS) {
return (EFAULT);
}
}
#endif
default: {
char event = 0;
if (sbd_ioctl)
else {
return (ENXIO);
}
/*
* Check to see if we need to send an event
*/
if (event == 1) {
int slot;
if (rv == 0) {
if (cmd == SBD_CMD_CONNECT ||
cmd == SBD_CMD_CONFIGURE)
else if (cmd == SBD_CMD_UNCONFIGURE ||
}
hint);
}
break;
}
}
return (rv);
}
void
{
/*
* Hold this mutex, until we are done so that ssm dip
* doesn't detach.
*/
continue;
break;
}
/* We didn't find the ssm dip, return failure */
attach_pnt[0] = '\0';
return;
}
/*
* we have the instance, and the board, construct the attch pnt
*/
if (SG_BOARD_IS_CPU_TYPE(board))
else
}
/*
* Generate an event to sysevent
*/
static int
{
int rv = 0;
attach_pnt[0] = '\0';
if (attach_pnt[0] == '\0')
return (-1);
KM_SLEEP);
if (rv != 0) {
return (rv);
}
/*
* Add the hint
*/
if (rv != 0) {
return (-1);
}
EC_DR);
return (-1);
}
if (rv != 0) {
}
return (rv);
}
/*
* DR Event Handler
*/
{
int hint;
("ssm_dr_event_handler: ARG is null\n"));
return (DDI_INTR_CLAIMED);
}
#ifdef DEBUG
#endif
switch (fdp->event_details) {
case SG_EVT_BOARD_ABSENT:
break;
case SG_EVT_BOARD_PRESENT:
break;
default:
hint = SE_NO_HINT;
break;
}
return (DDI_INTR_CLAIMED);
}
/*
* Initialize our FMA resources
*/
static void
{
/*
* Request or capability level and get our parents capability
* and ibc.
*/
/*
* Register error callback with our parent.
*/
}
/*
* Breakdown our FMA resources
*/
static void
{
/*
* Clean up allocated fm structures
*/
}
/*
* Initialize FMA resources for children devices. Called when
* child calls ddi_fm_init().
*/
/*ARGSUSED*/
static int
{
return (softsp->ssm_fm_cap);
}
/*
* FMA registered error callback
*/
/*ARGSUSED*/
static int
{
/* Call our children error handlers */
}