xref: /illumos-gate/usr/src/uts/common/io/1394/nx1394.c (revision 2570281c)
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 **)&regptr,
155 		    (uint_t *)&reglen);
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