1 /* $Id$
2  *
3  * This file is subject to the terms and conditions of the GNU General Public
4  * License.  See the file "COPYING" in the main directory of this archive
5  * for more details.
6  *
7  * Copyright (c) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved.
8  */
9 
10 #include <linux/types.h>
11 #include <linux/slab.h>
12 #include <asm/sn/sgi.h>
13 #include <asm/sn/driver.h>
14 #include <asm/sn/iograph.h>
15 #include <asm/sn/invent.h>
16 #include <asm/sn/hcl.h>
17 #include <asm/sn/labelcl.h>
18 #include <asm/sn/xtalk/xtalk.h>
19 #include <asm/sn/xtalk/xswitch.h>
20 #include <asm/sn/xtalk/xwidget.h>
21 #include <asm/sn/xtalk/xtalk_private.h>
22 
23 #define	NEW(ptr)	(ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
24 #define	DEL(ptr)	(kfree(ptr))
25 
26 /*
27  * This file provides generic support for Crosstalk
28  * Switches, in a way that insulates crosstalk providers
29  * from specifics about the switch chips being used.
30  */
31 
32 #include <asm/sn/xtalk/xbow.h>
33 #define DEV_FUNC(dev,func)      xbow_##func
34 
35 #if !defined(DEV_FUNC)
36 /*
37  * There is more than one possible provider
38  * for this platform. We need to examine the
39  * master vertex of the current vertex for
40  * a provider function structure, and indirect
41  * through the appropriately named member.
42  */
43 #define	DEV_FUNC(dev,func)	xwidget_to_provider_fns(dev)->func
44 
45 static xswitch_provider_t *
xwidget_to_provider_fns(vertex_hdl_t xconn)46 xwidget_to_provider_fns(vertex_hdl_t xconn)
47 {
48     vertex_hdl_t            busv;
49     xswitch_info_t          xswitch_info;
50     xswitch_provider_t      provider_fns;
51 
52     busv = hwgraph_connectpt_get(xconn_vhdl);
53     ASSERT(busv != GRAPH_VERTEX_NONE);
54 
55     xswitch_info = xswitch_info_get(busv);
56     ASSERT(xswitch_info != NULL);
57 
58     provider_fns = xswitch_info->xswitch_fns;
59     ASSERT(provider_fns != NULL);
60 
61     return provider_fns;
62 }
63 #endif
64 
65 #define	XSWITCH_CENSUS_BIT(port)		(1<<(port))
66 #define	XSWITCH_CENSUS_PORT_MIN			(0x0)
67 #define	XSWITCH_CENSUS_PORT_MAX			(0xF)
68 #define	XSWITCH_CENSUS_PORTS			(0x10)
69 #define	XSWITCH_WIDGET_PRESENT(infop,port)	((infop)->census & XSWITCH_CENSUS_BIT(port))
70 
71 static char             xswitch_info_fingerprint[] = "xswitch_info";
72 
73 struct xswitch_info_s {
74     char                   *fingerprint;
75     unsigned                census;
76     vertex_hdl_t            vhdl[XSWITCH_CENSUS_PORTS];
77     vertex_hdl_t            master_vhdl[XSWITCH_CENSUS_PORTS];
78     xswitch_provider_t     *xswitch_fns;
79 };
80 
81 xswitch_info_t
xswitch_info_get(vertex_hdl_t xwidget)82 xswitch_info_get(vertex_hdl_t xwidget)
83 {
84     xswitch_info_t          xswitch_info;
85 
86     xswitch_info = (xswitch_info_t)
87 	hwgraph_fastinfo_get(xwidget);
88 
89     return (xswitch_info);
90 }
91 
92 void
xswitch_info_vhdl_set(xswitch_info_t xswitch_info,xwidgetnum_t port,vertex_hdl_t xwidget)93 xswitch_info_vhdl_set(xswitch_info_t xswitch_info,
94 		      xwidgetnum_t port,
95 		      vertex_hdl_t xwidget)
96 {
97 #if XSWITCH_CENSUS_PORT_MIN
98     if (port < XSWITCH_CENSUS_PORT_MIN)
99 	return;
100 #endif
101     if (port > XSWITCH_CENSUS_PORT_MAX)
102 	return;
103 
104     xswitch_info->vhdl[port - XSWITCH_CENSUS_PORT_MIN] = xwidget;
105 }
106 
107 vertex_hdl_t
xswitch_info_vhdl_get(xswitch_info_t xswitch_info,xwidgetnum_t port)108 xswitch_info_vhdl_get(xswitch_info_t xswitch_info,
109 		      xwidgetnum_t port)
110 {
111 #if XSWITCH_CENSUS_PORT_MIN
112     if (port < XSWITCH_CENSUS_PORT_MIN)
113 	return GRAPH_VERTEX_NONE;
114 #endif
115     if (port > XSWITCH_CENSUS_PORT_MAX)
116 	return GRAPH_VERTEX_NONE;
117 
118     return xswitch_info->vhdl[port - XSWITCH_CENSUS_PORT_MIN];
119 }
120 
121 /*
122  * Some systems may allow for multiple switch masters.  On such systems,
123  * we assign a master for each port on the switch.  These interfaces
124  * establish and retrieve that assignment.
125  */
126 void
xswitch_info_master_assignment_set(xswitch_info_t xswitch_info,xwidgetnum_t port,vertex_hdl_t master_vhdl)127 xswitch_info_master_assignment_set(xswitch_info_t xswitch_info,
128 				   xwidgetnum_t port,
129 				   vertex_hdl_t master_vhdl)
130 {
131 #if XSWITCH_CENSUS_PORT_MIN
132     if (port < XSWITCH_CENSUS_PORT_MIN)
133 	return;
134 #endif
135     if (port > XSWITCH_CENSUS_PORT_MAX)
136 	return;
137 
138     xswitch_info->master_vhdl[port - XSWITCH_CENSUS_PORT_MIN] = master_vhdl;
139 }
140 
141 vertex_hdl_t
xswitch_info_master_assignment_get(xswitch_info_t xswitch_info,xwidgetnum_t port)142 xswitch_info_master_assignment_get(xswitch_info_t xswitch_info,
143 				   xwidgetnum_t port)
144 {
145 #if XSWITCH_CENSUS_PORT_MIN
146     if (port < XSWITCH_CENSUS_PORT_MIN)
147 	return GRAPH_VERTEX_NONE;
148 #endif
149     if (port > XSWITCH_CENSUS_PORT_MAX)
150 	return GRAPH_VERTEX_NONE;
151 
152     return xswitch_info->master_vhdl[port - XSWITCH_CENSUS_PORT_MIN];
153 }
154 
155 void
xswitch_info_set(vertex_hdl_t xwidget,xswitch_info_t xswitch_info)156 xswitch_info_set(vertex_hdl_t xwidget, xswitch_info_t xswitch_info)
157 {
158     xswitch_info->fingerprint = xswitch_info_fingerprint;
159     hwgraph_fastinfo_set(xwidget, (arbitrary_info_t) xswitch_info);
160 }
161 
162 xswitch_info_t
xswitch_info_new(vertex_hdl_t xwidget)163 xswitch_info_new(vertex_hdl_t xwidget)
164 {
165     xswitch_info_t          xswitch_info;
166 
167     xswitch_info = xswitch_info_get(xwidget);
168     if (xswitch_info == NULL) {
169 	int                     port;
170 
171 	NEW(xswitch_info);
172 	xswitch_info->census = 0;
173 	for (port = XSWITCH_CENSUS_PORT_MIN;
174 	     port <= XSWITCH_CENSUS_PORT_MAX;
175 	     port++) {
176 	    xswitch_info_vhdl_set(xswitch_info, port,
177 				  GRAPH_VERTEX_NONE);
178 
179 	    xswitch_info_master_assignment_set(xswitch_info,
180 					       port,
181 					       GRAPH_VERTEX_NONE);
182 	}
183 	xswitch_info_set(xwidget, xswitch_info);
184     }
185     return xswitch_info;
186 }
187 
188 void
xswitch_provider_register(vertex_hdl_t busv,xswitch_provider_t * xswitch_fns)189 xswitch_provider_register(vertex_hdl_t busv,
190 			  xswitch_provider_t * xswitch_fns)
191 {
192     xswitch_info_t          xswitch_info = xswitch_info_get(busv);
193 
194     ASSERT(xswitch_info);
195     xswitch_info->xswitch_fns = xswitch_fns;
196 }
197 
198 void
xswitch_info_link_is_ok(xswitch_info_t xswitch_info,xwidgetnum_t port)199 xswitch_info_link_is_ok(xswitch_info_t xswitch_info, xwidgetnum_t port)
200 {
201     xswitch_info->census |= XSWITCH_CENSUS_BIT(port);
202 }
203 
204 int
xswitch_info_link_ok(xswitch_info_t xswitch_info,xwidgetnum_t port)205 xswitch_info_link_ok(xswitch_info_t xswitch_info, xwidgetnum_t port)
206 {
207 #if XSWITCH_CENSUS_PORT_MIN
208     if (port < XSWITCH_CENSUS_PORT_MIN)
209 	return 0;
210 #endif
211 
212     if (port > XSWITCH_CENSUS_PORT_MAX)
213 	return 0;
214 
215     return (xswitch_info->census & XSWITCH_CENSUS_BIT(port));
216 }
217 
218 int
xswitch_reset_link(vertex_hdl_t xconn_vhdl)219 xswitch_reset_link(vertex_hdl_t xconn_vhdl)
220 {
221     return DEV_FUNC(xconn_vhdl, reset_link)
222 	(xconn_vhdl);
223 }
224