xref: /openbmc/linux/arch/mips/sgi-ip27/ip27-xtalk.c (revision b830f94f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
4  * Copyright (C) 1999, 2000 Silcon Graphics, Inc.
5  * Copyright (C) 2004 Christoph Hellwig.
6  *
7  * Generic XTALK initialization code
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/smp.h>
12 #include <linux/platform_device.h>
13 #include <linux/platform_data/xtalk-bridge.h>
14 #include <asm/sn/addrs.h>
15 #include <asm/sn/types.h>
16 #include <asm/sn/klconfig.h>
17 #include <asm/sn/hub.h>
18 #include <asm/pci/bridge.h>
19 #include <asm/xtalk/xtalk.h>
20 
21 
22 #define XBOW_WIDGET_PART_NUM	0x0
23 #define XXBOW_WIDGET_PART_NUM	0xd000	/* Xbow in Xbridge */
24 #define BASE_XBOW_PORT		8     /* Lowest external port */
25 
26 static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
27 {
28 	struct xtalk_bridge_platform_data *bd;
29 	struct platform_device *pdev;
30 	unsigned long offset;
31 
32 	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
33 	if (!bd)
34 		goto no_mem;
35 	pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
36 	if (!pdev) {
37 		kfree(bd);
38 		goto no_mem;
39 	}
40 
41 	offset = NODE_OFFSET(nasid);
42 
43 	bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
44 	bd->intr_addr	= BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
45 	bd->nasid	= nasid;
46 	bd->masterwid	= masterwid;
47 
48 	bd->mem.name	= "Bridge PCI MEM";
49 	bd->mem.start	= offset + (widget << SWIN_SIZE_BITS);
50 	bd->mem.end	= bd->mem.start + SWIN_SIZE - 1;
51 	bd->mem.flags	= IORESOURCE_MEM;
52 	bd->mem_offset	= offset;
53 
54 	bd->io.name	= "Bridge PCI IO";
55 	bd->io.start	= offset + (widget << SWIN_SIZE_BITS);
56 	bd->io.end	= bd->io.start + SWIN_SIZE - 1;
57 	bd->io.flags	= IORESOURCE_IO;
58 	bd->io_offset	= offset;
59 
60 	platform_device_add_data(pdev, bd, sizeof(*bd));
61 	platform_device_add(pdev);
62 	pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget);
63 	return;
64 
65 no_mem:
66 	pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
67 }
68 
69 static int probe_one_port(nasid_t nasid, int widget, int masterwid)
70 {
71 	widgetreg_t		widget_id;
72 	xwidget_part_num_t	partnum;
73 
74 	widget_id = *(volatile widgetreg_t *)
75 		(RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID);
76 	partnum = XWIDGET_PART_NUM(widget_id);
77 
78 	switch (partnum) {
79 	case BRIDGE_WIDGET_PART_NUM:
80 	case XBRIDGE_WIDGET_PART_NUM:
81 		bridge_platform_create(nasid, widget, masterwid);
82 		break;
83 	default:
84 		break;
85 	}
86 
87 	return 0;
88 }
89 
90 static int xbow_probe(nasid_t nasid)
91 {
92 	lboard_t *brd;
93 	klxbow_t *xbow_p;
94 	unsigned masterwid, i;
95 
96 	/*
97 	 * found xbow, so may have multiple bridges
98 	 * need to probe xbow
99 	 */
100 	brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
101 	if (!brd)
102 		return -ENODEV;
103 
104 	xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW);
105 	if (!xbow_p)
106 		return -ENODEV;
107 
108 	/*
109 	 * Okay, here's a xbow. Let's arbitrate and find
110 	 * out if we should initialize it. Set enabled
111 	 * hub connected at highest or lowest widget as
112 	 * master.
113 	 */
114 #ifdef WIDGET_A
115 	i = HUB_WIDGET_ID_MAX + 1;
116 	do {
117 		i--;
118 	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
119 		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
120 #else
121 	i = HUB_WIDGET_ID_MIN - 1;
122 	do {
123 		i++;
124 	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
125 		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
126 #endif
127 
128 	masterwid = i;
129 	if (nasid != XBOW_PORT_NASID(xbow_p, i))
130 		return 1;
131 
132 	for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) {
133 		if (XBOW_PORT_IS_ENABLED(xbow_p, i) &&
134 		    XBOW_PORT_TYPE_IO(xbow_p, i))
135 			probe_one_port(nasid, i, masterwid);
136 	}
137 
138 	return 0;
139 }
140 
141 static void xtalk_probe_node(cnodeid_t nid)
142 {
143 	volatile u64		hubreg;
144 	nasid_t			nasid;
145 	xwidget_part_num_t	partnum;
146 	widgetreg_t		widget_id;
147 
148 	nasid = COMPACT_TO_NASID_NODEID(nid);
149 	hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
150 
151 	/* check whether the link is up */
152 	if (!(hubreg & IIO_LLP_CSR_IS_UP))
153 		return;
154 
155 	widget_id = *(volatile widgetreg_t *)
156 		       (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
157 	partnum = XWIDGET_PART_NUM(widget_id);
158 
159 	switch (partnum) {
160 	case BRIDGE_WIDGET_PART_NUM:
161 		bridge_platform_create(nasid, 0x8, 0xa);
162 		break;
163 	case XBOW_WIDGET_PART_NUM:
164 	case XXBOW_WIDGET_PART_NUM:
165 		pr_info("xtalk:n%d/0 xbow widget\n", nasid);
166 		xbow_probe(nasid);
167 		break;
168 	default:
169 		pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum);
170 		break;
171 	}
172 }
173 
174 static int __init xtalk_init(void)
175 {
176 	cnodeid_t cnode;
177 
178 	for_each_online_node(cnode)
179 		xtalk_probe_node(cnode);
180 
181 	return 0;
182 }
183 arch_initcall(xtalk_init);
184