wrsm_driver.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file implements the standard DDI interface functions for
* the Wildcat RSM driver.
*/
#include <sys/machsystm.h>
/* Driver specific headers */
#include <sys/wci_common.h>
#include <sys/wci_regs.h>
#include <sys/wrsm_driver.h>
#include <sys/wrsm_plugin.h>
#include <sys/wrsm_memseg.h>
#include <sys/wrsm_memseg_impl.h>
#include <sys/wrsm_rsmpi.h>
/* Headers for modules that wrsm depends on */
#include <sys/wrsm_plat.h>
/*
* Exported data structures;
*/
static kmutex_t wrsm_attach_mutex;
static int wrsm_attachcnt = 0; /* # of instances attached */
/*
* Internal Function prototypes
*/
unsigned int, unsigned int, unsigned int, cred_t *);
/*
* Configuration data structures
*/
static struct cb_ops wrsm_cb_ops = {
wrsm_open, /* open */
wrsm_close, /* close */
nulldev, /* strategy */
nulldev, /* print */
nodev, /* dump */
nulldev, /* read */
nulldev, /* write */
wrsm_ioctl, /* ioctl */
wrsm_devmap, /* devmap */
nodev, /* mmap */
wrsm_segmap, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
};
static rsmops_registry_t wrsm_rsmops = {
"wrsm",
NULL /* rsm_thread_entry_pt */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
wrsm_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
wrsm_attach, /* attach */
wrsm_detach, /* detach */
nulldev, /* reset */
&wrsm_cb_ops, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
nulldev /* power */
};
static wrsm_plat_ops_t wrsm_plat_ops = {
wrsm_lc_phys_link_up, /* discovery success-msg from SC */
wrsm_lc_phys_link_down, /* link down - msg from SC */
wrsm_cf_sc_failed, /* report that the SC has reset */
wrsm_cf_lookup_wci, /* give lcwci_handle for wci id */
get_remote_config_data /* lookup remote config info */
};
/*
* Driver globals
*/
extern void wrsm_redist(void *);
static void *wrsm_softstates; /* wrsm soft state hook */
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"WRSM v%I%" /* name of module */
#ifdef DEBUG
" (Debug)"
#endif
,
&wrsm_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev */
(void *)&modldrv,
};
/*
* Controllers must be assigned a minor number that matches their
* controller id. This means that the device instance number can't be used
* for the minor number. The minor_to_instance table is used to translate
* between the 2. To ensure that lower numbered minor numbers are
* available (controllers ids are assigned starting with 0), the admin
* device is assigned a minor number of (MAX_INSTANCE - 1), and wci devices
* are assigned a minor number of (MAX_INSTANCE - 2 - instance#)
*/
#define MAX_INSTANCES 1024
static int minor_to_instance[MAX_INSTANCES];
#ifdef DEBUG
#define WRSM_DEBUG 0x0001
#define WRSM_WARN 0x0002
#else
#define DPRINTF(a, b) { }
#endif
/*
* *************************************************************************
*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
int i;
wrsmdbgnext = 0;
wrsmdbginit = 1;
#endif
for (i = 0; i < MAX_INSTANCES; i++) {
minor_to_instance[i] = -1;
}
/* Initialize soft state pointer. */
sizeof (wrsm_softstate_t), 1)) != 0) {
#endif
return (error);
}
/* Install the module. */
goto err_cleanup;
}
/*
* initialize the various RSM driver sub-modules
*/
wrsm_nc_init();
wrsm_cf_init();
/* register call backs with platform specific mailbox layer */
wrsm_cf_fini();
(void) wrsm_nc_fini();
goto err_cleanup;
}
/*
* register with RSM
*/
(void) wrsmplat_unreg_callbacks();
wrsm_cf_fini();
(void) wrsm_nc_fini();
goto err_cleanup;
}
return (WRSM_SUCCESS);
#endif
return (error);
}
int
_fini(void)
{
int error;
/*
* Make sure there are no RSMPI users before allowing driver to
* be removed.
*/
return (EBUSY);
}
/*
* Make sure there are no configurations installed before allowing
* driver to be removed.
*/
if (wrsm_nc_check() != WRSM_SUCCESS) {
(void) rsm_register_driver(&wrsm_rsmops);
return (EBUSY);
}
/* Prepare the module to be removed. */
(void) rsm_register_driver(&wrsm_rsmops);
return (error);
}
/*
* mod_remove() succeeded. We can do cleanup now.
*/
wrsm_cf_fini();
/* unregister call backs with platform specific module */
(void) wrsmplat_unreg_callbacks();
/* Free the soft state info. */
#endif
return (0);
}
int
{
}
/* device driver entry points */
/*
* Translate "dev_t" to a pointer to the associated "dev_info_t".
*/
/* ARGSUSED */
static int
{
int instance;
int minor;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (minor >= MAX_INSTANCES)
return (DDI_FAILURE);
if (instance == -1)
return (DDI_FAILURE);
== NULL) {
"ddi_get_soft_state failed for wrsm%d",
instance));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
if (minor >= MAX_INSTANCES)
return (DDI_FAILURE);
if (instance == -1)
return (DDI_FAILURE);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
int ret;
case wrsm_admin:
/* do nothing */
return (DDI_SUCCESS);
case wrsm_rsm_controller:
if (ret == WRSM_SUCCESS) {
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
default:
return (DDI_SUCCESS);
}
}
static int
{
char *special_name;
int minor;
char *ddi_type;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
== NULL) {
"DDI_RESUME - no device\n"));
return (DDI_FAILURE);
}
return (wrsm_do_resume(softsp));
default:
return (DDI_FAILURE);
}
/* Allocate soft data structure */
"for wrsm%d", instance);
return (DDI_FAILURE);
}
"ddi_get_soft_state failed for wrsm%d", instance));
return (DDI_FAILURE);
}
/* Set the devi in the soft state */
/*
* controller device
*/
if (minor >= MAX_INSTANCES) {
"can't support controller id %d "
"(instance >= %d)",
return (DDI_FAILURE);
}
"controller can't use minor dev %d "
"(in use by wrsm instance %d)",
return (DDI_FAILURE);
}
special_name = "ctrl";
instance));
/*
* admin device
*/
if (wrsm_admin_softsp) {
"is already attached (wrsm%d) - not "
"attaching wrsm%d",
return (DDI_FAILURE);
}
special_name = "admin";
/*
* record this devi in wrsm_ncslice_dip.
*/
} else {
/*
* wci device
*/
/* request safari port id (extended agent id from OBP */
return (DDI_FAILURE);
}
if (minor < 0) {
"instance out of range (>= %d) for wci %d",
return (DDI_FAILURE);
}
"wci can't use minor dev %d "
"(in use by wrsm instance %d)",
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* copy the regs base address into wci common soft state */
/* pre-calc virtual addr's for ecc registers */
/* pre-calculate virtual address of link error reg */
/* check if paroli present */
(link_no * STRIDE_WCI_SW_LINK_STATUS)));
/* no paroli module attached for this link */
} else {
}
}
}
/* pre-calc virtual addrs for Performance Counters registers */
(link_no * STRIDE_WCI_LINK_CTR));
}
/* map the sfari histogrammming counter registers */
/*
* Create the picN kstats if we are the first instance
* to attach. We use wrsm_attachcnt as a count of how
* many instances have attached. This is protected by
* a lock.
*/
if (wrsm_attachcnt++ == 0) {
/* add misc, lpbk, link picN kstats */
wci_add_picN_kstats("wrsm");
"wrsm-shortterm-interval",
"wrsm-shorts-per-longterm",
}
/* Create the counters kstats for this device */
/* Create the wci-links kstat for this device */
}
/* This creates the device node */
"node failed on wrsm%d", instance));
}
return (DDI_FAILURE);
}
/* wrsm_mutex used with driver open and close */
/*
* cmmu_mutex used during cmmu_update for CMMU_UPDATE_FLUSH flag
* as only one thread at a time may request a FLUSH (sync cmmu's)
*/
/* init to NOT opened - just to be paranoid */
/*
* create lock to protect the links, the link counters:
* link_req_cntdown, and link_req_cntup
* and to prevent changes made to the config in softsp->config
* while in process of executing config request
*/
/*
* Create condition var to allow installconfig to go once there
* are no outstanding link_takedown request pending.
* lc_cleanconfig will increment oldlink_waitdown_cnt for each
* takedown request there is in lc_cleanconfig.
* lc_installconfig will cv_wait until oldlink_waitdown_cnt =
* 0. lc_phys_link_down is responsible for signalling
* lc_installconfig if there are takedown request.
*/
"for wrsm%d", instance);
return (DDI_FAILURE);
}
/*
* Notify config layer that this RSM controller
* pseudo device is available
*/
WRSM_SUCCESS) {
" failed for wrsm%d", instance);
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
{
int ret;
case wrsm_admin:
/* do nothing */
return (DDI_SUCCESS);
case wrsm_rsm_controller:
if (ret == WRSM_SUCCESS) {
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
default:
return (DDI_SUCCESS);
}
}
static int
{
int instance, i;
switch (cmd) {
case DDI_SUSPEND:
break;
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
cmd));
return (DDI_FAILURE);
if (cmd == DDI_SUSPEND) {
return (wrsm_do_suspend(softsp));
}
case wrsm_rsm_controller:
"failed for wrsm%d", instance);
return (DDI_FAILURE);
}
break;
case wrsm_admin:
break;
default:
/* find out which controller (if any) this WCI belongs to */
/*
* If this wci has been claimed for external loopback
* testing then the owner will be WRSM_LOOPBACK_ID.
* If this is the case, then just shut down all the
* links.
*/
if (wci_owner == WRSM_LOOPBACK_ID) {
for (i = 0; i < WRSM_LINKS_PER_WCI; i++)
(void) wrsm_lc_loopback_disable(softsp, i);
}
" failed for wrsm%d", instance));
return (DDI_FAILURE);
}
/* check if links are all lc_down/lc_not_there */
for (i = 0; i < WRSM_LINKS_PER_WCI; i++) {
lc_not_there) {
"link in invalid state"));
(void) wrsm_cf_newwci(softsp,
return (DDI_FAILURE);
}
}
/* LINTED: E_NOP_IF_STMT */
" err_timeout_id not valid\n"));
}
/*
* See if we are the last instance to detach.
* If so, we need to remove the picN kstats
*/
if (--wrsm_attachcnt == 0) {
/* delete misc, lpbk, link picN kstats driver */
}
/* LINTED: E_NOP_IF_STMT */
" restart_timeout_id not valid\n"));
}
break;
}
/* release minor node */
/* destroy per instance mutex's */
/* release soft state */
/*
* create lock for a per link basis - needed to
* secure state change of softsp->links[i].link_req_state
*/
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
int instance;
int minor;
int retval = 0;
/* Verify we are being opened as a character device */
return (EINVAL);
if (minor >= MAX_INSTANCES)
return (ENXIO);
if (instance == -1)
return (ENXIO);
/* Verify instance structure */
return (ENXIO);
case wrsm_device:
"can't open, already opened exclusively"));
"can't open exclusively, already open"));
} else {
} else {
}
}
break;
case wrsm_rsm_controller:
/*
* controllers are opened by the wrsm plugin library
* librsmwrsm.so
*/
break;
default:
/*
* admin devices can also be opened, but there is no need
* for an exclusive check.
*/
break;
}
return (retval);
}
/* ARGSUSED */
static int
{
int instance;
int minor;
/* Verify we are being closed as a character device */
return (EINVAL);
if (minor >= MAX_INSTANCES)
return (ENXIO);
if (instance == -1)
return (ENXIO);
"for instance %d", instance);
return (ENXIO);
}
}
return (WRSM_SUCCESS);
}
/* ARGSUSED */
static int
int *rval_p)
{
struct wrsm_soft_state *softsp;
int minor;
int instance;
int retval;
if (minor >= MAX_INSTANCES)
return (ENXIO);
if (instance == -1)
return (ENXIO);
/* Verify instance structure */
return (ENXIO);
/*
* The 3 types of WCI devices support different ioctls. Use a
* different ioctl handling function for each type, so it's easier
* to fail on an unsupported ioctl.
*/
case wrsm_admin:
rval_p);
return (retval);
case wrsm_rsm_controller:
switch (cmd) {
/*
* the wrsm_smallput_plugin_ioctl is required
* functionality the so that the plugin library can
* perform smallputs.
*/
/*
* the plugin library needs to know if the export cnode
* is a local node
*/
default:
/* provided solely for debuging and testing */
}
default:
rval_p);
return (retval);
}
}
/* ARGSUSED */
static int
{
/*
* wrsm_segmap is currently handling only controller related segmaps.
* In the event other wrsm type devices (wci or admin( require the
* use of segmap, appropriate ddi calls to fetch the softstate
* structure will be needed. We know the call into the
* wrsm_memseg_segmap will fail for wci or admin devices because the
* minor dev won't map to a valid controller id.
*/
}
/* ARGSUSED */
static int
{
/*
* wrsm_devmap is currently handling only controller related segmaps.
* In the event other wrsm type devices (wci or admin( require the
* use of segmap, appropriate ddi calls to fetch the softstate
* structure will be needed. We know the call into the
* wrsm_memseg_devmap will fail for wci or admin devices because the
* minor dev won't map to a valid controller id.
*/
}
/*ARGSUSED*/
static int
{
int retval = 0;
int linkno;
/* Only allow privileged users to do this */
return (retval);
switch (cmd) {
case WRSM_WCI_LOOPBACK_ON:
break;
case WRSM_WCI_LOOPBACK_OFF:
break;
case WRSM_WCI_LINKTEST:
sizeof (wrsm_linktest_arg_t), flag) != 0) {
break;
}
break;
sizeof (wrsm_linktest_arg_t), flag) != 0)
break;
case WRSM_WCI_CLAIM:
break;
case WRSM_WCI_RELEASE:
/* find out which controller (if any) this WCI belongs to */
if (wci_owner != WRSM_LOOPBACK_ID) {
break;
}
return (0);
case WRSM_WCI_LINKUP:
break;
}
break;
case WRSM_WCI_LINKDOWN:
break;
}
break;
default:
break;
}
return (retval);
}
static int
{
volatile unsigned char *sram_vaddr;
/* Map in the device registers */
return (DDI_FAILURE);
}
/*
* since OBP provides SRAM as a register set
* we use ddi_map_regs to get the vaddr
* for the SRAM and then we use va_to_pa to get
* the physical addr.
*/
return (DDI_FAILURE);
}
0, 1);
return (DDI_FAILURE);
}
/* physical addr for sram */
return (DDI_SUCCESS);
}
static void
{
}
static void
{
struct kstat *status_ksp;
int instance;
int i;
char tmp_str[100];
/* Get the instance */
"bus", KSTAT_TYPE_NAMED,
sizeof (wrsm_status_kstat_t) / sizeof (kstat_named_t),
0)) == NULL) {
return;
}
/* initialize the named kstats */
for (i = 0; i < WCI_NUM_LINKS; i++) {
}
/* Save the kstat pointer in the softstate */
}
static int
{
int num_failed_bringups;
int i;
/* Device is not part of a controller */
/* Set all links to be invalid status */
for (i = 0; i < WCI_NUM_LINKS; i++) {
/*
* The rest of these fields should be ignored since
* link is marked "not present", but set to 0 just
* to be sure.
*/
0;
}
/* Set error kstats to 0 -- not applicable if not in ctrl */
return (WRSM_SUCCESS);
}
if (rw == KSTAT_WRITE) {
return (EACCES);
}
for (i = 0; i < WCI_NUM_LINKS; i++) {
/* read the wci_sw_controll register */
(i * STRIDE_WCI_SW_LINK_CONTROL),
/* link takedown stats */
/* note that avg_err is average * weight */
0 : 1;
/* shortterm interval count */
/* shortterm link error stats */
/* note that avg_errors is average * weight */
/* longterm link error stats */
/* note that avg_errors is average * weight */
/ wrsm_avg_weight);
/* set the auto_shutdown_en[i] field */
} else {
/* This link does not exist */
/*
* The rest of these fields should be ignored since
* link is marked "not present", but set to 0 just
* to be sure.
*/
0;
}
}
/* note that the avg_sram_ecc_errors is the average * weight */
return (WRSM_SUCCESS);
}
static void
{
}
/*
* translate from dip to network pointer
*/
{
int instance;
return (NULL);
}
return (NULL);
}
}