xref: /linux/arch/mips/sgi-ip27/ip27-xtalk.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
159bd9dedSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
41da177e4SLinus Torvalds  * Copyright (C) 1999, 2000 Silcon Graphics, Inc.
51da177e4SLinus Torvalds  * Copyright (C) 2004 Christoph Hellwig.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Generic XTALK initialization code
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/kernel.h>
11631330f5SRalf Baechle #include <linux/smp.h>
12a57140e9SThomas Bogendoerfer #include <linux/platform_device.h>
135dc76a96SThomas Bogendoerfer #include <linux/platform_data/sgi-w1.h>
14a57140e9SThomas Bogendoerfer #include <linux/platform_data/xtalk-bridge.h>
15a57140e9SThomas Bogendoerfer #include <asm/sn/addrs.h>
161da177e4SLinus Torvalds #include <asm/sn/types.h>
171da177e4SLinus Torvalds #include <asm/sn/klconfig.h>
181da177e4SLinus Torvalds #include <asm/pci/bridge.h>
191da177e4SLinus Torvalds #include <asm/xtalk/xtalk.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define XBOW_WIDGET_PART_NUM	0x0
231da177e4SLinus Torvalds #define XXBOW_WIDGET_PART_NUM	0xd000	/* Xbow in Xbridge */
241da177e4SLinus Torvalds #define BASE_XBOW_PORT		8     /* Lowest external port */
251da177e4SLinus Torvalds 
bridge_platform_create(nasid_t nasid,int widget,int masterwid)26a57140e9SThomas Bogendoerfer static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
27a57140e9SThomas Bogendoerfer {
28a57140e9SThomas Bogendoerfer 	struct xtalk_bridge_platform_data *bd;
295dc76a96SThomas Bogendoerfer 	struct sgi_w1_platform_data *wd;
30*11bec9cbSLin Yujun 	struct platform_device *pdev_wd;
31*11bec9cbSLin Yujun 	struct platform_device *pdev_bd;
325dc76a96SThomas Bogendoerfer 	struct resource w1_res;
33a57140e9SThomas Bogendoerfer 	unsigned long offset;
34a57140e9SThomas Bogendoerfer 
355dc76a96SThomas Bogendoerfer 	offset = NODE_OFFSET(nasid);
365dc76a96SThomas Bogendoerfer 
375dc76a96SThomas Bogendoerfer 	wd = kzalloc(sizeof(*wd), GFP_KERNEL);
38*11bec9cbSLin Yujun 	if (!wd) {
39*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
40*11bec9cbSLin Yujun 		return;
41*11bec9cbSLin Yujun 	}
425dc76a96SThomas Bogendoerfer 
435dc76a96SThomas Bogendoerfer 	snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
445dc76a96SThomas Bogendoerfer 		 offset + (widget << SWIN_SIZE_BITS));
455dc76a96SThomas Bogendoerfer 
465dc76a96SThomas Bogendoerfer 	memset(&w1_res, 0, sizeof(w1_res));
475dc76a96SThomas Bogendoerfer 	w1_res.start = offset + (widget << SWIN_SIZE_BITS) +
485dc76a96SThomas Bogendoerfer 				offsetof(struct bridge_regs, b_nic);
495dc76a96SThomas Bogendoerfer 	w1_res.end = w1_res.start + 3;
505dc76a96SThomas Bogendoerfer 	w1_res.flags = IORESOURCE_MEM;
515dc76a96SThomas Bogendoerfer 
52*11bec9cbSLin Yujun 	pdev_wd = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
53*11bec9cbSLin Yujun 	if (!pdev_wd) {
54*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
55*11bec9cbSLin Yujun 		goto err_kfree_wd;
565dc76a96SThomas Bogendoerfer 	}
57*11bec9cbSLin Yujun 	if (platform_device_add_resources(pdev_wd, &w1_res, 1)) {
58*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge failed to add platform resources.\n", nasid, widget);
59*11bec9cbSLin Yujun 		goto err_put_pdev_wd;
60*11bec9cbSLin Yujun 	}
61*11bec9cbSLin Yujun 	if (platform_device_add_data(pdev_wd, wd, sizeof(*wd))) {
62*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
63*11bec9cbSLin Yujun 		goto err_put_pdev_wd;
64*11bec9cbSLin Yujun 	}
65*11bec9cbSLin Yujun 	if (platform_device_add(pdev_wd)) {
66*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
67*11bec9cbSLin Yujun 		goto err_put_pdev_wd;
68*11bec9cbSLin Yujun 	}
6933d70856SChristophe JAILLET 	/* platform_device_add_data() duplicates the data */
7033d70856SChristophe JAILLET 	kfree(wd);
715dc76a96SThomas Bogendoerfer 
72a57140e9SThomas Bogendoerfer 	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
73*11bec9cbSLin Yujun 	if (!bd) {
74*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
75*11bec9cbSLin Yujun 		goto err_unregister_pdev_wd;
76*11bec9cbSLin Yujun 	}
77*11bec9cbSLin Yujun 	pdev_bd = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
78*11bec9cbSLin Yujun 	if (!pdev_bd) {
79*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
80*11bec9cbSLin Yujun 		goto err_kfree_bd;
81a57140e9SThomas Bogendoerfer 	}
82a57140e9SThomas Bogendoerfer 
83a57140e9SThomas Bogendoerfer 
84a57140e9SThomas Bogendoerfer 	bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
85a57140e9SThomas Bogendoerfer 	bd->intr_addr	= BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
86a57140e9SThomas Bogendoerfer 	bd->nasid	= nasid;
87a57140e9SThomas Bogendoerfer 	bd->masterwid	= masterwid;
88a57140e9SThomas Bogendoerfer 
89a57140e9SThomas Bogendoerfer 	bd->mem.name	= "Bridge PCI MEM";
905dc76a96SThomas Bogendoerfer 	bd->mem.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
915dc76a96SThomas Bogendoerfer 	bd->mem.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
92a57140e9SThomas Bogendoerfer 	bd->mem.flags	= IORESOURCE_MEM;
93a57140e9SThomas Bogendoerfer 	bd->mem_offset	= offset;
94a57140e9SThomas Bogendoerfer 
95a57140e9SThomas Bogendoerfer 	bd->io.name	= "Bridge PCI IO";
965dc76a96SThomas Bogendoerfer 	bd->io.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
975dc76a96SThomas Bogendoerfer 	bd->io.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
98a57140e9SThomas Bogendoerfer 	bd->io.flags	= IORESOURCE_IO;
99a57140e9SThomas Bogendoerfer 	bd->io_offset	= offset;
100a57140e9SThomas Bogendoerfer 
101*11bec9cbSLin Yujun 	if (platform_device_add_data(pdev_bd, bd, sizeof(*bd))) {
102*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
103*11bec9cbSLin Yujun 		goto err_put_pdev_bd;
104*11bec9cbSLin Yujun 	}
105*11bec9cbSLin Yujun 	if (platform_device_add(pdev_bd)) {
106*11bec9cbSLin Yujun 		pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
107*11bec9cbSLin Yujun 		goto err_put_pdev_bd;
108*11bec9cbSLin Yujun 	}
10933d70856SChristophe JAILLET 	/* platform_device_add_data() duplicates the data */
11033d70856SChristophe JAILLET 	kfree(bd);
111a57140e9SThomas Bogendoerfer 	pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget);
112a57140e9SThomas Bogendoerfer 	return;
113a57140e9SThomas Bogendoerfer 
114*11bec9cbSLin Yujun err_put_pdev_bd:
115*11bec9cbSLin Yujun 	platform_device_put(pdev_bd);
116*11bec9cbSLin Yujun err_kfree_bd:
117*11bec9cbSLin Yujun 	kfree(bd);
118*11bec9cbSLin Yujun err_unregister_pdev_wd:
119*11bec9cbSLin Yujun 	platform_device_unregister(pdev_wd);
120*11bec9cbSLin Yujun 	return;
121*11bec9cbSLin Yujun err_put_pdev_wd:
122*11bec9cbSLin Yujun 	platform_device_put(pdev_wd);
123*11bec9cbSLin Yujun err_kfree_wd:
124*11bec9cbSLin Yujun 	kfree(wd);
125*11bec9cbSLin Yujun 	return;
126a57140e9SThomas Bogendoerfer }
1271da177e4SLinus Torvalds 
probe_one_port(nasid_t nasid,int widget,int masterwid)128078a55fcSPaul Gortmaker static int probe_one_port(nasid_t nasid, int widget, int masterwid)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	widgetreg_t		widget_id;
1311da177e4SLinus Torvalds 	xwidget_part_num_t	partnum;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	widget_id = *(volatile widgetreg_t *)
1341da177e4SLinus Torvalds 		(RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID);
1351da177e4SLinus Torvalds 	partnum = XWIDGET_PART_NUM(widget_id);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	switch (partnum) {
1381da177e4SLinus Torvalds 	case BRIDGE_WIDGET_PART_NUM:
1391da177e4SLinus Torvalds 	case XBRIDGE_WIDGET_PART_NUM:
140a57140e9SThomas Bogendoerfer 		bridge_platform_create(nasid, widget, masterwid);
1411da177e4SLinus Torvalds 		break;
1421da177e4SLinus Torvalds 	default:
1435dc76a96SThomas Bogendoerfer 		pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
1445dc76a96SThomas Bogendoerfer 			nasid, widget, partnum);
1451da177e4SLinus Torvalds 		break;
1461da177e4SLinus Torvalds 	}
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	return 0;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
xbow_probe(nasid_t nasid)151078a55fcSPaul Gortmaker static int xbow_probe(nasid_t nasid)
1521da177e4SLinus Torvalds {
1531da177e4SLinus Torvalds 	lboard_t *brd;
1541da177e4SLinus Torvalds 	klxbow_t *xbow_p;
1551da177e4SLinus Torvalds 	unsigned masterwid, i;
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	/*
1581da177e4SLinus Torvalds 	 * found xbow, so may have multiple bridges
1591da177e4SLinus Torvalds 	 * need to probe xbow
1601da177e4SLinus Torvalds 	 */
1611da177e4SLinus Torvalds 	brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
1621da177e4SLinus Torvalds 	if (!brd)
1631da177e4SLinus Torvalds 		return -ENODEV;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW);
1661da177e4SLinus Torvalds 	if (!xbow_p)
1671da177e4SLinus Torvalds 		return -ENODEV;
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	/*
1704939788eSRalf Baechle 	 * Okay, here's a xbow. Let's arbitrate and find
1711da177e4SLinus Torvalds 	 * out if we should initialize it. Set enabled
1721da177e4SLinus Torvalds 	 * hub connected at highest or lowest widget as
1731da177e4SLinus Torvalds 	 * master.
1741da177e4SLinus Torvalds 	 */
1751da177e4SLinus Torvalds #ifdef WIDGET_A
1761da177e4SLinus Torvalds 	i = HUB_WIDGET_ID_MAX + 1;
1771da177e4SLinus Torvalds 	do {
1781da177e4SLinus Torvalds 		i--;
1791da177e4SLinus Torvalds 	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
1801da177e4SLinus Torvalds 		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
1811da177e4SLinus Torvalds #else
1821da177e4SLinus Torvalds 	i = HUB_WIDGET_ID_MIN - 1;
1831da177e4SLinus Torvalds 	do {
1841da177e4SLinus Torvalds 		i++;
1851da177e4SLinus Torvalds 	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
1861da177e4SLinus Torvalds 		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
1871da177e4SLinus Torvalds #endif
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	masterwid = i;
1901da177e4SLinus Torvalds 	if (nasid != XBOW_PORT_NASID(xbow_p, i))
1911da177e4SLinus Torvalds 		return 1;
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) {
1941da177e4SLinus Torvalds 		if (XBOW_PORT_IS_ENABLED(xbow_p, i) &&
1951da177e4SLinus Torvalds 		    XBOW_PORT_TYPE_IO(xbow_p, i))
1961da177e4SLinus Torvalds 			probe_one_port(nasid, i, masterwid);
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	return 0;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
xtalk_probe_node(nasid_t nasid)2024bf841ebSThomas Bogendoerfer static void xtalk_probe_node(nasid_t nasid)
2031da177e4SLinus Torvalds {
2041da177e4SLinus Torvalds 	volatile u64		hubreg;
2051da177e4SLinus Torvalds 	xwidget_part_num_t	partnum;
2061da177e4SLinus Torvalds 	widgetreg_t		widget_id;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	/* check whether the link is up */
2111da177e4SLinus Torvalds 	if (!(hubreg & IIO_LLP_CSR_IS_UP))
2121da177e4SLinus Torvalds 		return;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 	widget_id = *(volatile widgetreg_t *)
2151da177e4SLinus Torvalds 		       (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
2161da177e4SLinus Torvalds 	partnum = XWIDGET_PART_NUM(widget_id);
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds 	switch (partnum) {
2191da177e4SLinus Torvalds 	case BRIDGE_WIDGET_PART_NUM:
220a57140e9SThomas Bogendoerfer 		bridge_platform_create(nasid, 0x8, 0xa);
2211da177e4SLinus Torvalds 		break;
2221da177e4SLinus Torvalds 	case XBOW_WIDGET_PART_NUM:
2231da177e4SLinus Torvalds 	case XXBOW_WIDGET_PART_NUM:
224a57140e9SThomas Bogendoerfer 		pr_info("xtalk:n%d/0 xbow widget\n", nasid);
2251da177e4SLinus Torvalds 		xbow_probe(nasid);
2261da177e4SLinus Torvalds 		break;
2271da177e4SLinus Torvalds 	default:
228a57140e9SThomas Bogendoerfer 		pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum);
2291da177e4SLinus Torvalds 		break;
2301da177e4SLinus Torvalds 	}
2311da177e4SLinus Torvalds }
2329707b7e6SThomas Bogendoerfer 
xtalk_init(void)2339707b7e6SThomas Bogendoerfer static int __init xtalk_init(void)
2349707b7e6SThomas Bogendoerfer {
2354bf841ebSThomas Bogendoerfer 	nasid_t nasid;
2369707b7e6SThomas Bogendoerfer 
2374bf841ebSThomas Bogendoerfer 	for_each_online_node(nasid)
2384bf841ebSThomas Bogendoerfer 		xtalk_probe_node(nasid);
2399707b7e6SThomas Bogendoerfer 
2409707b7e6SThomas Bogendoerfer 	return 0;
2419707b7e6SThomas Bogendoerfer }
2429707b7e6SThomas Bogendoerfer arch_initcall(xtalk_init);
243