/* * 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. */ /* * Copyright 2012 Garrett D'Amore . All rights reserved. */ /* * nx1394.c * 1394 Services Layer Nexus Support Routines * Routines in this file implement nexus bus_ops. */ #include #include #include #include #include #include #include #include #include #include #include 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 */ NULL, /* 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 **)®ptr, (uint_t *)®len); 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_SIDDEV: case DDI_CTLOPS_SLAVEONLY: case DDI_CTLOPS_AFFINITY: 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, ""); }