nx1394.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
* or http://www.opensolaris.org/os/licensing.
* 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"
/*
* nx1394.c
* 1394 Services Layer Nexus Support Routines
* Routines in this file implement nexus bus_ops.
*/
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/types.h>
#include <sys/ddi_impldefs.h>
#include <sys/tnf_probe.h>
#include <sys/1394/t1394.h>
#include <sys/1394/s1394.h>
#include <sys/1394/h1394.h>
static int nx1394_dma_allochdl(dev_info_t *dip, dev_info_t *rdip,
ddi_dma_attr_t *attr, int (*waitfnp)(caddr_t), caddr_t arg,
ddi_dma_handle_t *handlep);
static int nx1394_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
void *arg, void *result);
static int nx1394_get_event_cookie(dev_info_t *dip, dev_info_t *rdip,
char *name, ddi_eventcookie_t *event_cookiep);
static int nx1394_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
ddi_eventcookie_t eventhdl, void (*callback)(), void *arg,
ddi_callback_id_t *cb_id);
static int nx1394_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id);
static int nx1394_post_event(dev_info_t *dip, dev_info_t *rdip,
ddi_eventcookie_t eventhdl, void *impl_data);
struct bus_ops nx1394_busops = {
BUSO_REV,
nullbusmap, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
i_ddi_map_fault, /* XXXX bus_map_fault */
ddi_dma_map, /* bus_dma_map */
nx1394_dma_allochdl,
ddi_dma_freehdl,
ddi_dma_bindhdl,
ddi_dma_unbindhdl,
ddi_dma_flush,
ddi_dma_win,
ddi_dma_mctl, /* bus_dma_ctl */
nx1394_bus_ctl, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
nx1394_get_event_cookie, /* (*bus_get_eventcookie() */
nx1394_add_eventcall, /* (*bus_add_eventcall)(); */
nx1394_remove_eventcall, /* (*bus_remove_eventcall)(); */
nx1394_post_event, /* (*bus_post_event)(); */
0, /* (*interrupt control)(); */
0, /* (*bus_config)(); */
0, /* (*bus_unconfig)(); */
0, /* (*bus_fm_init)(); */
0, /* (*bus_fm_fini)(); */
0, /* (*bus_fm_access_enter)(); */
0, /* (*bus_fm_access_exit)(); */
0, /* (*bus_power)(); */
i_ddi_intr_ops /* (*bus_intr_op)(); */
};
/*
* removal/insertion/reset events
*/
#define NX1394_EVENT_TAG_HOT_REMOVAL 0
#define NX1394_EVENT_TAG_HOT_INSERTION 1
#define NX1394_EVENT_TAG_BUS_RESET 2
static ndi_event_definition_t nx1394_event_defs[] = {
{NX1394_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
NDI_EVENT_POST_TO_TGT},
{NX1394_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
NDI_EVENT_POST_TO_TGT},
{NX1394_EVENT_TAG_BUS_RESET, DDI_DEVI_BUS_RESET_EVENT, EPL_KERNEL,
NDI_EVENT_POST_TO_ALL},
};
#define NX1394_N_EVENTS \
(sizeof (nx1394_event_defs) / sizeof (ndi_event_definition_t))
static ndi_event_set_t nx1394_events = {
NDI_EVENTS_REV1, NX1394_N_EVENTS, nx1394_event_defs
};
/*
* nx1394_bus_ctl()
* This routine implements nexus bus ctl operations. Of importance are
* DDI_CTLOPS_REPORTDEV, DDI_CTLOPS_INITCHILD, DDI_CTLOPS_UNINITCHILD
* and DDI_CTLOPS_POWER. For DDI_CTLOPS_INITCHILD, it tries to lookup
* reg property on the child node and builds and sets the name
* (name is of the form GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA], where
* GGGGGGGGGGGGGGGG is the GUID and AAAAAAAAAAAA is the optional unit
* address).
*/
static int
nx1394_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
void *result)
{
int status;
TNF_PROBE_0_DEBUG(nx1394_bus_ctl_enter, S1394_TNF_SL_NEXUS_STACK, "");
switch (op) {
case DDI_CTLOPS_REPORTDEV: {
dev_info_t *pdip = ddi_get_parent(rdip);
cmn_err(CE_CONT, "?%s%d at %s%d",
ddi_node_name(rdip), ddi_get_instance(rdip),
ddi_node_name(pdip), ddi_get_instance(pdip));
TNF_PROBE_0_DEBUG(nx1394_bus_ctl_exit, S1394_TNF_SL_NEXUS_STACK,
"");
return (DDI_SUCCESS);
}
case DDI_CTLOPS_INITCHILD: {
dev_info_t *ocdip, *cdip = (dev_info_t *)arg;
dev_info_t *pdip = ddi_get_parent(cdip);
int reglen, i;
uint32_t *regptr;
char addr[MAXNAMELEN];
TNF_PROBE_1(nx1394_bus_ctl_init_child,
S1394_TNF_SL_HOTPLUG_STACK, "", tnf_opaque, dip, cdip);
i = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
DDI_PROP_DONTPASS, "reg", (int **)&regptr,
(uint_t *)&reglen);
if (i != DDI_PROP_SUCCESS) {
cmn_err(CE_NOTE, "!%s(%d): \"reg\" property not found",
ddi_node_name(cdip), ddi_get_instance(cdip));
TNF_PROBE_2(nx1394_bus_ctl,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_string, msg,
"Reg property not found", tnf_int, reason, i);
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string, op,
"initchild");
return (DDI_NOT_WELL_FORMED);
}
ASSERT(reglen != 0);
/*
* addr is of the format GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA]
*/
if (regptr[2] || regptr[3]) {
(void) sprintf(addr, "%08x%08x,%04x%08x", regptr[0],
regptr[1], regptr[2], regptr[3]);
} else {
(void) sprintf(addr, "%08x%08x", regptr[0], regptr[1]);
}
ddi_prop_free(regptr);
ddi_set_name_addr(cdip, addr);
/*
* Check for a node with the same name & addr as the current
* node. If such a node exists, return failure.
*/
if ((ocdip = ndi_devi_find(pdip, ddi_node_name(cdip), addr)) !=
NULL && ocdip != cdip) {
cmn_err(CE_NOTE,
"!%s(%d): Duplicate dev_info node found %s@%s",
ddi_node_name(cdip), ddi_get_instance(cdip),
ddi_node_name(ocdip), addr);
TNF_PROBE_1(nx1394_bus_ctl,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_string, msg,
"Duplicate nodes");
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string, op,
"initchild");
ddi_set_name_addr(cdip, NULL);
return (DDI_NOT_WELL_FORMED);
}
/*
* If HAL (parent dip) has "active-dma-flush" property, then
* add property to child as well. Workaround for active
* context flushing bug in Schizo rev 2.1 and 2.2.
*/
if (ddi_prop_exists(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
"active-dma-flush") != 0) {
status = ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
"active-dma-flush", 1);
if (status != NDI_SUCCESS) {
cmn_err(CE_NOTE, "!%s(%d): Unable to add "
"\"active-dma-flush\" property",
ddi_node_name(cdip),
ddi_get_instance(cdip));
TNF_PROBE_1(nx1394_bus_ctl,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_string,
msg, "Unable to add \"active-dma-flush\" "
"property");
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string,
op, "initchild");
ddi_set_name_addr(cdip, NULL);
return (DDI_NOT_WELL_FORMED);
}
}
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string, op, "initchild");
return (DDI_SUCCESS);
}
case DDI_CTLOPS_UNINITCHILD: {
ddi_prop_remove_all((dev_info_t *)arg);
ddi_set_name_addr((dev_info_t *)arg, NULL);
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit, S1394_TNF_SL_NEXUS_STACK,
"", tnf_string, op, "uninitchild");
return (DDI_SUCCESS);
}
case DDI_CTLOPS_IOMIN: {
status = ddi_ctlops(dip, rdip, op, arg, result);
TNF_PROBE_1_DEBUG(nx1394_bus_ctl_exit, S1394_TNF_SL_NEXUS_STACK,
"", tnf_string, op, "iomin");
return (status);
}
case DDI_CTLOPS_POWER: {
return (DDI_SUCCESS);
}
/*
* These ops correspond to functions that "shouldn't" be called
* by a 1394 client driver.
*/
case DDI_CTLOPS_DMAPMAPC:
case DDI_CTLOPS_REPORTINT:
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
case DDI_CTLOPS_NINTRS:
case DDI_CTLOPS_SIDDEV:
case DDI_CTLOPS_SLAVEONLY:
case DDI_CTLOPS_AFFINITY:
case DDI_CTLOPS_INTR_HILEVEL:
case DDI_CTLOPS_XLATE_INTRS:
case DDI_CTLOPS_POKE:
case DDI_CTLOPS_PEEK: {
cmn_err(CE_CONT, "!%s(%d): invalid op (%d) from %s(%d)",
ddi_node_name(dip), ddi_get_instance(dip),
op, ddi_node_name(rdip), ddi_get_instance(rdip));
TNF_PROBE_2(nx1394_bus_ctl, S1394_TNF_SL_NEXUS_ERROR, "",
tnf_string, msg, "invalid op", tnf_int, op, op);
TNF_PROBE_0_DEBUG(nx1394_bus_ctl_exit, S1394_TNF_SL_NEXUS_STACK,
"");
return (DDI_FAILURE);
}
/*
* Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
*/
default: {
status = ddi_ctlops(dip, rdip, op, arg, result);
TNF_PROBE_0_DEBUG(nx1394_bus_ctl_exit, S1394_TNF_SL_NEXUS_STACK,
"");
return (status);
}
}
}
/*
* nx1394_dma_allochdl()
* Merges the ddi_dma_attr_t passed in by the target (using
* ddi_dma_alloc_handle() call) with that of the hal and passes the alloc
* handle request up the device by calling ddi_dma_allochdl().
*/
static int
nx1394_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
int (*waitfnp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep)
{
s1394_hal_t *hal;
ddi_dma_attr_t *hal_attr;
int status;
_NOTE(SCHEME_PROTECTS_DATA("unique (per thread)", ddi_dma_attr_t))
TNF_PROBE_0_DEBUG(nx1394_dma_allochdl_enter, S1394_TNF_SL_NEXUS_STACK,
"");
/*
* If hal calls ddi_dma_alloc_handle, dip == rdip == hal dip.
* Unfortunately, we cannot verify this (by way of looking up for hal
* dip) here because h1394_attach() may happen much later.
*/
if (dip != rdip) {
hal = s1394_dip_to_hal(ddi_get_parent(rdip));
ASSERT(hal);
hal_attr = &hal->halinfo.dma_attr;
ASSERT(hal_attr);
ddi_dma_attr_merge(attr, hal_attr);
}
status = ddi_dma_allochdl(dip, rdip, attr, waitfnp, arg, handlep);
TNF_PROBE_1_DEBUG(nx1394_dma_allochdl_exit, S1394_TNF_SL_NEXUS_STACK,
"", tnf_int, status, status);
return (status);
}
/*
* nx1394_get_event_cookie()
* Called when a child node calls ddi_get_eventcookie().
* Returns event cookie corresponding to event "name".
*/
static int
nx1394_get_event_cookie(dev_info_t *dip, dev_info_t *rdip, char *name,
ddi_eventcookie_t *event_cookiep)
{
int ret;
s1394_hal_t *hal;
TNF_PROBE_1_DEBUG(nx1394_get_event_cookie_enter,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string, name, name);
hal = s1394_dip_to_hal(dip);
ASSERT(hal);
ret = ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl,
rdip, name, event_cookiep, 0);
TNF_PROBE_4_DEBUG(nx1394_get_event_cookie_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_opaque, parent_dip, (void *)dip,
tnf_opaque, requestor_dip, (void *)rdip, tnf_string, event_name,
name, tnf_int, request_status, ret);
return (ret);
}
/*
* nx1394_add_eventcall()
* This gets called when a child node calls ddi_add_eventcall(). Registers
* the specified callback for the requested event cookie with the ndi
* event framework.
* dip is the hal dip. This routine calls ndi_event_add_callback(),
* allowing requests for events we don't generate to pass up the tree.
*/
static int
nx1394_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
ddi_eventcookie_t cookie, void (*callback)(), void *arg,
ddi_callback_id_t *cb_id)
{
int ret;
s1394_hal_t *hal;
#if defined(DEBUG)
char *event_name = NULL;
#endif
hal = s1394_dip_to_hal(dip);
ASSERT(hal);
TNF_PROBE_0_DEBUG(nx1394_add_eventcall_enter, S1394_TNF_SL_NEXUS_STACK,
"");
ret = ndi_event_add_callback(hal->hal_ndi_event_hdl, rdip, cookie,
callback, arg, NDI_NOSLEEP, cb_id);
#if defined(DEBUG)
event_name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie);
if (event_name == NULL)
event_name = "";
#endif
TNF_PROBE_4_DEBUG(nx1394_add_eventcall_exit, S1394_TNF_SL_NEXUS_STACK,
"", tnf_opaque, parent_dip, (void *)dip, tnf_opaque, requestor_dip,
(void *)rdip, tnf_string, event_name, event_name, tnf_int,
request_status, ret);
return (ret);
}
/*
* nx1394_remove_eventcall()
* Called as a result of a child node calling ddi_remove_eventcall().
* Unregisters the callback corresponding to the callback id passed in.
*/
static int
nx1394_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
{
int ret;
s1394_hal_t *hal;
ddi_eventcookie_t cookie;
#if defined(DEBUG)
char *event_name = NULL;
#endif
ASSERT(cb_id);
cookie = ((ndi_event_callbacks_t *)cb_id)->ndi_evtcb_cookie;
hal = s1394_dip_to_hal(dip);
ASSERT(hal);
TNF_PROBE_0_DEBUG(nx1394_remove_eventcall_enter,
S1394_TNF_SL_NEXUS_STACK, "");
ret = ndi_event_remove_callback(hal->hal_ndi_event_hdl, cb_id);
#if defined(DEBUG)
event_name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie);
if (event_name == NULL)
event_name = "";
TNF_PROBE_4_DEBUG(nx1394_remove_eventcall_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_opaque, parent_dip, (void *)dip,
tnf_opaque, callback_id, (void *)cb_id, tnf_string, event_name,
event_name, tnf_int, request_status, ret);
#endif
return (ret);
}
/*
* nx1394_post_event()
* Called when a child node calls ddi_post_event. If the event is one of
* the events supported by us (bus reset/insert/remove, for now), builds
* a t1394_localinfo_t structure and calls ndi_event_run_callbacks(). This
* will result in all registered callbacks being invoked with
* t1394_localinfo_t as the impl_data. (see ddi_add_eventcall for callback
* arguments.) If the event is not defined by us, the request is
* propagated up the device tree by calling ndi_post_event().
*/
static int
nx1394_post_event(dev_info_t *dip, dev_info_t *rdip, ddi_eventcookie_t cookie,
void *impl_data)
{
int ret;
char *name;
s1394_hal_t *hal;
t1394_localinfo_t localinfo;
hal = s1394_dip_to_hal(dip);
ASSERT(hal);
TNF_PROBE_0_DEBUG(nx1394_post_event_enter, S1394_TNF_SL_NEXUS_STACK,
"");
name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie);
/* name is NULL if we don't generate the event */
if (name != NULL) {
mutex_enter(&hal->topology_tree_mutex);
localinfo.bus_generation = hal->generation_count;
localinfo.local_nodeID = hal->node_id;
mutex_exit(&hal->topology_tree_mutex);
impl_data = &localinfo;
ret = ndi_event_run_callbacks(hal->hal_ndi_event_hdl,
rdip, cookie, impl_data);
TNF_PROBE_4_DEBUG(nx1394_post_event_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_opaque, parent_dip,
(void *)dip, tnf_opaque, requestor_dip, (void *)rdip,
tnf_string, event_name, name, tnf_int, request_status, ret);
return (ret);
} else {
ret = ndi_post_event(ddi_get_parent(dip), rdip, cookie,
impl_data);
TNF_PROBE_2_DEBUG(nx1394_post_event_exit,
S1394_TNF_SL_NEXUS_STACK, "", tnf_string, msg,
"Not our event", tnf_int, ret, ret);
return (ret);
}
}
/*
* nx1394_define_events()
* Allocates event handle for the hal dip and binds event set to it.
*/
int
nx1394_define_events(s1394_hal_t *hal)
{
int ret;
TNF_PROBE_0_DEBUG(nx1394_define_events_enter, S1394_TNF_SL_NEXUS_STACK,
"");
/* get event handle */
ret = ndi_event_alloc_hdl(hal->halinfo.dip, hal->halinfo.hw_interrupt,
&hal->hal_ndi_event_hdl, NDI_SLEEP);
if (ret != NDI_SUCCESS) {
TNF_PROBE_1(nx1394_define_events_alloc_fail,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_int, ret, ret);
} else {
/* and bind to it */
ret = ndi_event_bind_set(hal->hal_ndi_event_hdl, &nx1394_events,
NDI_SLEEP);
if (ret != NDI_SUCCESS) {
TNF_PROBE_1(nx1394_define_events_bind_fail,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_int, ret, ret);
(void) ndi_event_free_hdl(hal->hal_ndi_event_hdl);
TNF_PROBE_0_DEBUG(nx1394_define_events_exit,
S1394_TNF_SL_NEXUS_STACK, "");
return (DDI_FAILURE);
}
}
TNF_PROBE_0_DEBUG(nx1394_define_events_exit, S1394_TNF_SL_NEXUS_STACK,
"");
return (DDI_SUCCESS);
}
/*
* nx1394_undefine_events()
* Unbinds event set bound to the hal and frees the event handle.
*/
void
nx1394_undefine_events(s1394_hal_t *hal)
{
int ret;
TNF_PROBE_0_DEBUG(nx1394_undefine_events_enter,
S1394_TNF_SL_NEXUS_STACK, "");
ret = ndi_event_unbind_set(hal->hal_ndi_event_hdl, &nx1394_events,
NDI_SLEEP);
if (ret != NDI_SUCCESS) {
TNF_PROBE_1(nx1394_undefine_events_unbind_fail,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_int, ret, ret);
} else {
ret = ndi_event_free_hdl(hal->hal_ndi_event_hdl);
if (ret != NDI_SUCCESS) {
TNF_PROBE_1(nx1394_undefine_events_free_hdl_fail,
S1394_TNF_SL_NEXUS_ERROR, "", tnf_int, ret, ret);
}
}
TNF_PROBE_0_DEBUG(nx1394_undefine_events_exit,
S1394_TNF_SL_NEXUS_STACK, "");
}