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
nx1394_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)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
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)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
nx1394_get_event_cookie(dev_info_t * dip,dev_info_t * rdip,char * name,ddi_eventcookie_t * event_cookiep)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
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)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
nx1394_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)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
nx1394_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * impl_data)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
nx1394_define_events(s1394_hal_t * hal)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
nx1394_undefine_events(s1394_hal_t * hal)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