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