4dba132ca4caa67dff628e9e02a01a331821bf72
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / io / sn2 / pic.c
1 /*
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) 2001-2003 Silicon Graphics, Inc. All rights reserved.
8  */
9
10 #include <linux/types.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <asm/sn/sgi.h>
14 #include <asm/sn/sn_cpuid.h>
15 #include <asm/sn/addrs.h>
16 #include <asm/sn/arch.h>
17 #include <asm/sn/iograph.h>
18 #include <asm/sn/invent.h>
19 #include <asm/sn/hcl.h>
20 #include <asm/sn/labelcl.h>
21 #include <asm/sn/xtalk/xwidget.h>
22 #include <asm/sn/pci/bridge.h>
23 #include <asm/sn/pci/pciio.h>
24 #include <asm/sn/pci/pcibr.h>
25 #include <asm/sn/pci/pcibr_private.h>
26 #include <asm/sn/pci/pci_defs.h>
27 #include <asm/sn/prio.h>
28 #include <asm/sn/xtalk/xbow.h>
29 #include <asm/sn/ioc3.h>
30 #include <asm/sn/eeprom.h>
31 #include <asm/sn/io.h>
32 #include <asm/sn/sn_private.h>
33
34 extern char *bcopy(const char * src, char * dest, int count);
35
36
37 #define PCI_BUS_NO_1 1
38
39 int pic_devflag = D_MP;
40
41 extern int pcibr_attach2(devfs_handle_t, bridge_t *, devfs_handle_t, int, pcibr_soft_t *);
42 extern void pcibr_driver_reg_callback(devfs_handle_t, int, int, int);
43 extern void pcibr_driver_unreg_callback(devfs_handle_t, int, int, int);
44
45
46 void
47 pic_init(void)
48 {
49         PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INIT, NULL, "pic_init()\n"));   
50
51         xwidget_driver_register(PIC_WIDGET_PART_NUM_BUS0,
52                             PIC_WIDGET_MFGR_NUM,
53                             "pic_",
54                             0);
55 }
56
57 /*
58  * copy inventory_t from conn_v to peer_conn_v
59  */
60 int
61 pic_bus1_inventory_dup(devfs_handle_t conn_v, devfs_handle_t peer_conn_v)
62 {
63         inventory_t *pinv, *peer_pinv;
64
65         if (hwgraph_info_get_LBL(conn_v, INFO_LBL_INVENT,
66                                 (arbitrary_info_t *)&pinv) == GRAPH_SUCCESS)
67  {
68                 NEW(peer_pinv);
69                 bcopy(pinv, peer_pinv, sizeof(inventory_t));
70                 if (hwgraph_info_add_LBL(peer_conn_v, INFO_LBL_INVENT,
71                             (arbitrary_info_t)peer_pinv) != GRAPH_SUCCESS) {
72                         DEL(peer_pinv);
73                         return 0;
74                 }
75                 return 1;
76         }
77
78         printk("pic_bus1_inventory_dup: cannot get INFO_LBL_INVENT from 0x%lx\n ",
79                                                                 conn_v);
80         return 0;
81 }
82
83 /*
84  * copy xwidget_info_t from conn_v to peer_conn_v
85  */
86 int
87 pic_bus1_widget_info_dup(devfs_handle_t conn_v, devfs_handle_t peer_conn_v,
88                                                         cnodeid_t xbow_peer)
89 {
90         xwidget_info_t widget_info, peer_widget_info;
91         char peer_path[256];
92         char *p;
93         devfs_handle_t peer_hubv;
94         hubinfo_t peer_hub_info;
95
96         /* get the peer hub's widgetid */
97         peer_hubv = NODEPDA(xbow_peer)->node_vertex;
98         peer_hub_info = NULL;
99         hubinfo_get(peer_hubv, &peer_hub_info);
100         if (peer_hub_info == NULL)
101                 return 0;
102
103         if (hwgraph_info_get_LBL(conn_v, INFO_LBL_XWIDGET,
104                         (arbitrary_info_t *)&widget_info) == GRAPH_SUCCESS) {
105                 NEW(peer_widget_info);
106                 peer_widget_info->w_vertex = peer_conn_v;
107                 peer_widget_info->w_id = widget_info->w_id;
108                 peer_widget_info->w_master = peer_hubv;
109                 peer_widget_info->w_masterid = peer_hub_info->h_widgetid;
110                 /* structure copy */
111                 peer_widget_info->w_hwid = widget_info->w_hwid;
112                 peer_widget_info->w_efunc = 0;
113                 peer_widget_info->w_einfo = 0;
114                 peer_widget_info->w_name = kmalloc(strlen(peer_path) + 1, GFP_KERNEL);
115                 strcpy(peer_widget_info->w_name, peer_path);
116
117                 if (hwgraph_info_add_LBL(peer_conn_v, INFO_LBL_XWIDGET,
118                         (arbitrary_info_t)peer_widget_info) != GRAPH_SUCCESS) {
119                                 DEL(peer_widget_info);
120                                 return 0;
121                 }
122
123                 xwidget_info_set(peer_conn_v, peer_widget_info);
124
125                 return 1;
126         }
127
128         printk("pic_bus1_widget_info_dup: "
129                         "cannot get INFO_LBL_XWIDGET from 0x%lx\n", conn_v);
130         return 0;
131 }
132
133 /*
134  * If this PIC is attached to two Cbricks ("dual-ported") then
135  * attach each bus to opposite Cbricks.
136  *
137  * If successful, return a new vertex suitable for attaching the PIC bus.
138  * If not successful, return zero and both buses will attach to the
139  * vertex passed into pic_attach().
140  */
141 devfs_handle_t
142 pic_bus1_redist(nasid_t nasid, devfs_handle_t conn_v)
143 {
144         cnodeid_t cnode = NASID_TO_COMPACT_NODEID(nasid);
145         cnodeid_t xbow_peer = -1;
146         char pathname[256], peer_path[256], tmpbuf[256];
147         char *p;
148         int rc;
149         devfs_handle_t peer_conn_v;
150         int pos;
151         slabid_t slab;
152
153         if (NODEPDA(cnode)->xbow_peer >= 0) {                   /* if dual-ported */
154                 /* create a path for this widget on the peer Cbrick */
155                 /* pcibr widget hw/module/001c11/slab/0/Pbrick/xtalk/12 */
156                 /* sprintf(pathname, "%v", conn_v); */
157                 xbow_peer = NASID_TO_COMPACT_NODEID(NODEPDA(cnode)->xbow_peer);
158                 pos = devfs_generate_path(conn_v, tmpbuf, 256);
159                 strcpy(pathname, &tmpbuf[pos]);
160                 p = pathname + strlen("hw/module/001c01/slab/0/");
161
162                 memset(tmpbuf, 0, 16);
163                 format_module_id(tmpbuf, geo_module((NODEPDA(xbow_peer))->geoid), MODULE_FORMAT_BRIEF);
164                 slab = geo_slab((NODEPDA(xbow_peer))->geoid);
165                 sprintf(peer_path, "module/%s/slab/%d/%s", tmpbuf, (int)slab, p); 
166                 
167                 /* Look for vertex for this widget on the peer Cbrick.
168                  * Expect GRAPH_NOT_FOUND.
169                  */
170                 rc = hwgraph_traverse(hwgraph_root, peer_path, &peer_conn_v);
171                 if (GRAPH_SUCCESS == rc)
172                         printk("pic_attach: found unexpected vertex: 0x%lx\n",
173                                                                 peer_conn_v);
174                 else if (GRAPH_NOT_FOUND != rc) {
175                         printk("pic_attach: hwgraph_traverse unexpectedly"
176                                         " returned 0x%x\n", rc);
177                 } else {
178                         /* try to add the widget vertex to the peer Cbrick */
179                         rc = hwgraph_path_add(hwgraph_root, peer_path, &peer_conn_v);
180
181                         if (GRAPH_SUCCESS != rc)
182                             printk("pic_attach: hwgraph_path_add"
183                                                 " failed with 0x%x\n", rc);
184                         else {
185                             PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
186                                         "pic_bus1_redist: added vertex %v\n", peer_conn_v)); 
187
188                             /* Now hang appropiate stuff off of the new
189                              * vertex.  We bail out if we cannot add something.
190                              * In that case, we don't remove the newly added
191                              * vertex but that should be safe and we don't
192                              * really expect the additions to fail anyway.
193                              */
194 #if 0
195                             if (!pic_bus1_inventory_dup(conn_v, peer_conn_v))
196                                         return 0;
197                             pic_bus1_device_desc_dup(conn_v, peer_conn_v);
198 #endif
199                             if (!pic_bus1_widget_info_dup(conn_v, peer_conn_v, xbow_peer))
200                                         return 0;
201
202                             return peer_conn_v;
203                         }
204                 }
205         }
206         return 0;
207 }
208
209
210 int
211 pic_attach(devfs_handle_t conn_v)
212 {
213         int             rc;
214         bridge_t        *bridge0, *bridge1 = (bridge_t *)0;
215         devfs_handle_t  pcibr_vhdl0, pcibr_vhdl1 = (devfs_handle_t)0;
216         pcibr_soft_t    bus0_soft, bus1_soft = (pcibr_soft_t)0;
217         devfs_handle_t  conn_v0, conn_v1, peer_conn_v;
218
219         PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v, "pic_attach()\n"));
220
221         bridge0 = (bridge_t *) xtalk_piotrans_addr(conn_v, NULL,
222                                 0, sizeof(bridge_t), 0);
223         bridge1 = (bridge_t *)((char *)bridge0 + PIC_BUS1_OFFSET);
224
225         PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
226                     "pic_attach: bridge0=0x%x, bridge1=0x%x\n", 
227                     bridge0, bridge1));
228
229         conn_v0 = conn_v1 = conn_v;
230
231         /* If dual-ported then split the two PIC buses across both Cbricks */
232         if (peer_conn_v = pic_bus1_redist(NASID_GET(bridge0), conn_v))
233                 conn_v1 = peer_conn_v;
234
235         /*
236          * Create the vertex for the PCI buses, which week
237          * will also use to hold the pcibr_soft and
238          * which will be the "master" vertex for all the
239          * pciio connection points we will hang off it.
240          * This needs to happen before we call nic_bridge_vertex_info
241          * as we are some of the *_vmc functions need access to the edges.
242          *
243          * Opening this vertex will provide access to
244          * the Bridge registers themselves.
245          */
246         /* FIXME: what should the hwgraph path look like ? */
247         rc = hwgraph_path_add(conn_v0, EDGE_LBL_PCIX_0, &pcibr_vhdl0);
248         ASSERT(rc == GRAPH_SUCCESS);
249         rc = hwgraph_path_add(conn_v1, EDGE_LBL_PCIX_1, &pcibr_vhdl1);
250         ASSERT(rc == GRAPH_SUCCESS);
251
252         PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
253                     "pic_attach: pcibr_vhdl0=%v, pcibr_vhdl1=%v\n",
254                     pcibr_vhdl0, pcibr_vhdl1));
255
256         /* register pci provider array */
257         pciio_provider_register(pcibr_vhdl0, &pci_pic_provider);
258         pciio_provider_register(pcibr_vhdl1, &pci_pic_provider);
259
260         pciio_provider_startup(pcibr_vhdl0);
261         pciio_provider_startup(pcibr_vhdl1);
262
263         pcibr_attach2(conn_v0, bridge0, pcibr_vhdl0, 0, &bus0_soft);
264         pcibr_attach2(conn_v1, bridge1, pcibr_vhdl1, 1, &bus1_soft);
265
266         /* save a pointer to the PIC's other bus's soft struct */
267         bus0_soft->bs_peers_soft = bus1_soft;
268         bus1_soft->bs_peers_soft = bus0_soft;
269         bus0_soft->bs_peers_soft = (pcibr_soft_t)0;
270
271         PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ATTACH, conn_v,
272                     "pic_attach: bus0_soft=0x%x, bus1_soft=0x%x\n",
273                     bus0_soft, bus1_soft));
274
275         return 0;
276 }
277
278 /*
279  * pci provider functions
280  *
281  * mostly in pcibr.c but if any are needed here then
282  * this might be a way to get them here.
283  */
284 pciio_provider_t        pci_pic_provider =
285 {
286     (pciio_piomap_alloc_f *) pcibr_piomap_alloc,
287     (pciio_piomap_free_f *) pcibr_piomap_free,
288     (pciio_piomap_addr_f *) pcibr_piomap_addr,
289     (pciio_piomap_done_f *) pcibr_piomap_done,
290     (pciio_piotrans_addr_f *) pcibr_piotrans_addr,
291     (pciio_piospace_alloc_f *) pcibr_piospace_alloc,
292     (pciio_piospace_free_f *) pcibr_piospace_free,
293
294     (pciio_dmamap_alloc_f *) pcibr_dmamap_alloc,
295     (pciio_dmamap_free_f *) pcibr_dmamap_free,
296     (pciio_dmamap_addr_f *) pcibr_dmamap_addr,
297     (pciio_dmamap_list_f *) pcibr_dmamap_list,
298     (pciio_dmamap_done_f *) pcibr_dmamap_done,
299     (pciio_dmatrans_addr_f *) pcibr_dmatrans_addr,
300     (pciio_dmatrans_list_f *) pcibr_dmatrans_list,
301     (pciio_dmamap_drain_f *) pcibr_dmamap_drain,
302     (pciio_dmaaddr_drain_f *) pcibr_dmaaddr_drain,
303     (pciio_dmalist_drain_f *) pcibr_dmalist_drain,
304
305     (pciio_intr_alloc_f *) pcibr_intr_alloc,
306     (pciio_intr_free_f *) pcibr_intr_free,
307     (pciio_intr_connect_f *) pcibr_intr_connect,
308     (pciio_intr_disconnect_f *) pcibr_intr_disconnect,
309     (pciio_intr_cpu_get_f *) pcibr_intr_cpu_get,
310
311     (pciio_provider_startup_f *) pcibr_provider_startup,
312     (pciio_provider_shutdown_f *) pcibr_provider_shutdown,
313     (pciio_reset_f *) pcibr_reset,
314     (pciio_write_gather_flush_f *) pcibr_write_gather_flush,
315     (pciio_endian_set_f *) pcibr_endian_set,
316     (pciio_priority_set_f *) pcibr_priority_set,
317     (pciio_config_get_f *) pcibr_config_get,
318     (pciio_config_set_f *) pcibr_config_set,
319     (pciio_error_devenable_f *) 0,
320     (pciio_error_extract_f *) 0,
321     (pciio_driver_reg_callback_f *) pcibr_driver_reg_callback,
322     (pciio_driver_unreg_callback_f *) pcibr_driver_unreg_callback,
323     (pciio_device_unregister_f  *) pcibr_device_unregister,
324     (pciio_dma_enabled_f                *) pcibr_dma_enabled,
325 };