1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 28 */ 29 30 /* 31 * nx1394.c 32 * 1394 Services Layer Nexus Support Routines 33 * Routines in this file implement nexus bus_ops. 34 */ 35 36 #include <sys/conf.h> 37 #include <sys/ddi.h> 38 #include <sys/modctl.h> 39 #include <sys/sunddi.h> 40 #include <sys/cmn_err.h> 41 #include <sys/types.h> 42 #include <sys/ddi_impldefs.h> 43 #include <sys/1394/t1394.h> 44 #include <sys/1394/s1394.h> 45 #include <sys/1394/h1394.h> 46 47 static int nx1394_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 48 ddi_dma_attr_t *attr, int (*waitfnp)(caddr_t), caddr_t arg, 49 ddi_dma_handle_t *handlep); 50 51 static int nx1394_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 52 void *arg, void *result); 53 54 static int nx1394_get_event_cookie(dev_info_t *dip, dev_info_t *rdip, 55 char *name, ddi_eventcookie_t *event_cookiep); 56 57 static int nx1394_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 58 ddi_eventcookie_t eventhdl, void (*callback)(), void *arg, 59 ddi_callback_id_t *cb_id); 60 61 static int nx1394_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id); 62 63 static int nx1394_post_event(dev_info_t *dip, dev_info_t *rdip, 64 ddi_eventcookie_t eventhdl, void *impl_data); 65 66 struct bus_ops nx1394_busops = { 67 BUSO_REV, 68 nullbusmap, /* bus_map */ 69 NULL, /* bus_get_intrspec */ 70 NULL, /* bus_add_intrspec */ 71 NULL, /* bus_remove_intrspec */ 72 i_ddi_map_fault, /* XXXX bus_map_fault */ 73 NULL, /* bus_dma_map */ 74 nx1394_dma_allochdl, 75 ddi_dma_freehdl, 76 ddi_dma_bindhdl, 77 ddi_dma_unbindhdl, 78 ddi_dma_flush, 79 ddi_dma_win, 80 ddi_dma_mctl, /* bus_dma_ctl */ 81 nx1394_bus_ctl, /* bus_ctl */ 82 ddi_bus_prop_op, /* bus_prop_op */ 83 nx1394_get_event_cookie, /* (*bus_get_eventcookie() */ 84 nx1394_add_eventcall, /* (*bus_add_eventcall)(); */ 85 nx1394_remove_eventcall, /* (*bus_remove_eventcall)(); */ 86 nx1394_post_event, /* (*bus_post_event)(); */ 87 0, /* (*interrupt control)(); */ 88 0, /* (*bus_config)(); */ 89 0, /* (*bus_unconfig)(); */ 90 0, /* (*bus_fm_init)(); */ 91 0, /* (*bus_fm_fini)(); */ 92 0, /* (*bus_fm_access_enter)(); */ 93 0, /* (*bus_fm_access_exit)(); */ 94 0, /* (*bus_power)(); */ 95 i_ddi_intr_ops /* (*bus_intr_op)(); */ 96 }; 97 98 /* 99 * removal/insertion/reset events 100 */ 101 #define NX1394_EVENT_TAG_HOT_REMOVAL 0 102 #define NX1394_EVENT_TAG_HOT_INSERTION 1 103 #define NX1394_EVENT_TAG_BUS_RESET 2 104 105 static ndi_event_definition_t nx1394_event_defs[] = { 106 {NX1394_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, 107 NDI_EVENT_POST_TO_TGT}, 108 {NX1394_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL, 109 NDI_EVENT_POST_TO_TGT}, 110 {NX1394_EVENT_TAG_BUS_RESET, DDI_DEVI_BUS_RESET_EVENT, EPL_KERNEL, 111 NDI_EVENT_POST_TO_ALL}, 112 }; 113 114 #define NX1394_N_EVENTS \ 115 (sizeof (nx1394_event_defs) / sizeof (ndi_event_definition_t)) 116 117 static ndi_event_set_t nx1394_events = { 118 NDI_EVENTS_REV1, NX1394_N_EVENTS, nx1394_event_defs 119 }; 120 121 /* 122 * nx1394_bus_ctl() 123 * This routine implements nexus bus ctl operations. Of importance are 124 * DDI_CTLOPS_REPORTDEV, DDI_CTLOPS_INITCHILD, DDI_CTLOPS_UNINITCHILD 125 * and DDI_CTLOPS_POWER. For DDI_CTLOPS_INITCHILD, it tries to lookup 126 * reg property on the child node and builds and sets the name 127 * (name is of the form GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA], where 128 * GGGGGGGGGGGGGGGG is the GUID and AAAAAAAAAAAA is the optional unit 129 * address). 130 */ 131 static int 132 nx1394_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, 133 void *result) 134 { 135 int status; 136 137 switch (op) { 138 case DDI_CTLOPS_REPORTDEV: { 139 dev_info_t *pdip = ddi_get_parent(rdip); 140 cmn_err(CE_CONT, "?%s%d at %s%d", 141 ddi_node_name(rdip), ddi_get_instance(rdip), 142 ddi_node_name(pdip), ddi_get_instance(pdip)); 143 return (DDI_SUCCESS); 144 } 145 146 case DDI_CTLOPS_INITCHILD: { 147 dev_info_t *ocdip, *cdip = (dev_info_t *)arg; 148 dev_info_t *pdip = ddi_get_parent(cdip); 149 int reglen, i; 150 uint32_t *regptr; 151 char addr[MAXNAMELEN]; 152 153 i = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 154 DDI_PROP_DONTPASS, "reg", (int **)®ptr, 155 (uint_t *)®len); 156 157 if (i != DDI_PROP_SUCCESS) { 158 cmn_err(CE_NOTE, "!%s(%d): \"reg\" property not found", 159 ddi_node_name(cdip), ddi_get_instance(cdip)); 160 return (DDI_NOT_WELL_FORMED); 161 } 162 163 ASSERT(reglen != 0); 164 165 /* 166 * addr is of the format GGGGGGGGGGGGGGGG[,AAAAAAAAAAAA] 167 */ 168 if (regptr[2] || regptr[3]) { 169 (void) sprintf(addr, "%08x%08x,%04x%08x", regptr[0], 170 regptr[1], regptr[2], regptr[3]); 171 } else { 172 (void) sprintf(addr, "%08x%08x", regptr[0], regptr[1]); 173 } 174 ddi_prop_free(regptr); 175 ddi_set_name_addr(cdip, addr); 176 177 /* 178 * Check for a node with the same name & addr as the current 179 * node. If such a node exists, return failure. 180 */ 181 if ((ocdip = ndi_devi_find(pdip, ddi_node_name(cdip), addr)) != 182 NULL && ocdip != cdip) { 183 cmn_err(CE_NOTE, 184 "!%s(%d): Duplicate dev_info node found %s@%s", 185 ddi_node_name(cdip), ddi_get_instance(cdip), 186 ddi_node_name(ocdip), addr); 187 ddi_set_name_addr(cdip, NULL); 188 return (DDI_NOT_WELL_FORMED); 189 } 190 191 /* 192 * If HAL (parent dip) has "active-dma-flush" property, then 193 * add property to child as well. Workaround for active 194 * context flushing bug in Schizo rev 2.1 and 2.2. 195 */ 196 if (ddi_prop_exists(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 197 "active-dma-flush") != 0) { 198 status = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, 199 "active-dma-flush", 1); 200 if (status != NDI_SUCCESS) { 201 cmn_err(CE_NOTE, "!%s(%d): Unable to add " 202 "\"active-dma-flush\" property", 203 ddi_node_name(cdip), 204 ddi_get_instance(cdip)); 205 ddi_set_name_addr(cdip, NULL); 206 return (DDI_NOT_WELL_FORMED); 207 } 208 } 209 210 return (DDI_SUCCESS); 211 } 212 213 case DDI_CTLOPS_UNINITCHILD: { 214 ddi_prop_remove_all((dev_info_t *)arg); 215 ddi_set_name_addr((dev_info_t *)arg, NULL); 216 return (DDI_SUCCESS); 217 } 218 219 case DDI_CTLOPS_IOMIN: { 220 status = ddi_ctlops(dip, rdip, op, arg, result); 221 return (status); 222 } 223 224 case DDI_CTLOPS_POWER: { 225 return (DDI_SUCCESS); 226 } 227 228 /* 229 * These ops correspond to functions that "shouldn't" be called 230 * by a 1394 client driver. 231 */ 232 case DDI_CTLOPS_DMAPMAPC: 233 case DDI_CTLOPS_REPORTINT: 234 case DDI_CTLOPS_REGSIZE: 235 case DDI_CTLOPS_NREGS: 236 case DDI_CTLOPS_SIDDEV: 237 case DDI_CTLOPS_SLAVEONLY: 238 case DDI_CTLOPS_AFFINITY: 239 case DDI_CTLOPS_POKE: 240 case DDI_CTLOPS_PEEK: { 241 cmn_err(CE_CONT, "!%s(%d): invalid op (%d) from %s(%d)", 242 ddi_node_name(dip), ddi_get_instance(dip), 243 op, ddi_node_name(rdip), ddi_get_instance(rdip)); 244 return (DDI_FAILURE); 245 } 246 247 /* 248 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up 249 */ 250 default: { 251 status = ddi_ctlops(dip, rdip, op, arg, result); 252 return (status); 253 } 254 } 255 } 256 257 /* 258 * nx1394_dma_allochdl() 259 * Merges the ddi_dma_attr_t passed in by the target (using 260 * ddi_dma_alloc_handle() call) with that of the hal and passes the alloc 261 * handle request up the device by calling ddi_dma_allochdl(). 262 */ 263 static int 264 nx1394_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, 265 int (*waitfnp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 266 { 267 s1394_hal_t *hal; 268 ddi_dma_attr_t *hal_attr; 269 int status; 270 271 _NOTE(SCHEME_PROTECTS_DATA("unique (per thread)", ddi_dma_attr_t)) 272 273 /* 274 * If hal calls ddi_dma_alloc_handle, dip == rdip == hal dip. 275 * Unfortunately, we cannot verify this (by way of looking up for hal 276 * dip) here because h1394_attach() may happen much later. 277 */ 278 if (dip != rdip) { 279 hal = s1394_dip_to_hal(ddi_get_parent(rdip)); 280 ASSERT(hal); 281 hal_attr = &hal->halinfo.dma_attr; 282 ASSERT(hal_attr); 283 ddi_dma_attr_merge(attr, hal_attr); 284 } 285 status = ddi_dma_allochdl(dip, rdip, attr, waitfnp, arg, handlep); 286 return (status); 287 } 288 289 /* 290 * nx1394_get_event_cookie() 291 * Called when a child node calls ddi_get_eventcookie(). 292 * Returns event cookie corresponding to event "name". 293 */ 294 static int 295 nx1394_get_event_cookie(dev_info_t *dip, dev_info_t *rdip, char *name, 296 ddi_eventcookie_t *event_cookiep) 297 { 298 int ret; 299 s1394_hal_t *hal; 300 301 hal = s1394_dip_to_hal(dip); 302 ASSERT(hal); 303 304 ret = ndi_event_retrieve_cookie(hal->hal_ndi_event_hdl, 305 rdip, name, event_cookiep, 0); 306 307 return (ret); 308 309 } 310 311 /* 312 * nx1394_add_eventcall() 313 * This gets called when a child node calls ddi_add_eventcall(). Registers 314 * the specified callback for the requested event cookie with the ndi 315 * event framework. 316 * dip is the hal dip. This routine calls ndi_event_add_callback(), 317 * allowing requests for events we don't generate to pass up the tree. 318 */ 319 static int 320 nx1394_add_eventcall(dev_info_t *dip, dev_info_t *rdip, 321 ddi_eventcookie_t cookie, void (*callback)(), void *arg, 322 ddi_callback_id_t *cb_id) 323 { 324 int ret; 325 s1394_hal_t *hal; 326 #if defined(DEBUG) 327 char *event_name = NULL; 328 #endif 329 330 hal = s1394_dip_to_hal(dip); 331 ASSERT(hal); 332 333 ret = ndi_event_add_callback(hal->hal_ndi_event_hdl, rdip, cookie, 334 callback, arg, NDI_NOSLEEP, cb_id); 335 #if defined(DEBUG) 336 event_name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie); 337 if (event_name == NULL) 338 event_name = ""; 339 #endif 340 341 return (ret); 342 } 343 344 /* 345 * nx1394_remove_eventcall() 346 * Called as a result of a child node calling ddi_remove_eventcall(). 347 * Unregisters the callback corresponding to the callback id passed in. 348 */ 349 static int 350 nx1394_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id) 351 { 352 int ret; 353 s1394_hal_t *hal; 354 ddi_eventcookie_t cookie; 355 #if defined(DEBUG) 356 char *event_name = NULL; 357 #endif 358 359 ASSERT(cb_id); 360 cookie = ((ndi_event_callbacks_t *)cb_id)->ndi_evtcb_cookie; 361 362 hal = s1394_dip_to_hal(dip); 363 ASSERT(hal); 364 365 ret = ndi_event_remove_callback(hal->hal_ndi_event_hdl, cb_id); 366 367 #if defined(DEBUG) 368 event_name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie); 369 if (event_name == NULL) 370 event_name = ""; 371 372 #endif 373 374 return (ret); 375 } 376 377 /* 378 * nx1394_post_event() 379 * Called when a child node calls ddi_post_event. If the event is one of 380 * the events supported by us (bus reset/insert/remove, for now), builds 381 * a t1394_localinfo_t structure and calls ndi_event_run_callbacks(). This 382 * will result in all registered callbacks being invoked with 383 * t1394_localinfo_t as the impl_data. (see ddi_add_eventcall for callback 384 * arguments.) If the event is not defined by us, the request is 385 * propagated up the device tree by calling ndi_post_event(). 386 */ 387 static int 388 nx1394_post_event(dev_info_t *dip, dev_info_t *rdip, ddi_eventcookie_t cookie, 389 void *impl_data) 390 { 391 int ret; 392 char *name; 393 s1394_hal_t *hal; 394 t1394_localinfo_t localinfo; 395 396 hal = s1394_dip_to_hal(dip); 397 ASSERT(hal); 398 399 name = ndi_event_cookie_to_name(hal->hal_ndi_event_hdl, cookie); 400 /* name is NULL if we don't generate the event */ 401 if (name != NULL) { 402 403 mutex_enter(&hal->topology_tree_mutex); 404 localinfo.bus_generation = hal->generation_count; 405 localinfo.local_nodeID = hal->node_id; 406 mutex_exit(&hal->topology_tree_mutex); 407 impl_data = &localinfo; 408 409 ret = ndi_event_run_callbacks(hal->hal_ndi_event_hdl, 410 rdip, cookie, impl_data); 411 412 return (ret); 413 414 } else { 415 ret = ndi_post_event(ddi_get_parent(dip), rdip, cookie, 416 impl_data); 417 return (ret); 418 } 419 } 420 421 /* 422 * nx1394_define_events() 423 * Allocates event handle for the hal dip and binds event set to it. 424 */ 425 int 426 nx1394_define_events(s1394_hal_t *hal) 427 { 428 int ret; 429 430 /* get event handle */ 431 ret = ndi_event_alloc_hdl(hal->halinfo.dip, hal->halinfo.hw_interrupt, 432 &hal->hal_ndi_event_hdl, NDI_SLEEP); 433 if (ret == NDI_SUCCESS) { 434 /* and bind to it */ 435 ret = ndi_event_bind_set(hal->hal_ndi_event_hdl, &nx1394_events, 436 NDI_SLEEP); 437 if (ret != NDI_SUCCESS) { 438 (void) ndi_event_free_hdl(hal->hal_ndi_event_hdl); 439 return (DDI_FAILURE); 440 } 441 } 442 443 return (DDI_SUCCESS); 444 } 445 446 /* 447 * nx1394_undefine_events() 448 * Unbinds event set bound to the hal and frees the event handle. 449 */ 450 void 451 nx1394_undefine_events(s1394_hal_t *hal) 452 { 453 int ret; 454 455 ret = ndi_event_unbind_set(hal->hal_ndi_event_hdl, &nx1394_events, 456 NDI_SLEEP); 457 if (ret == NDI_SUCCESS) 458 ret = ndi_event_free_hdl(hal->hal_ndi_event_hdl); 459 } 460