18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
48c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000 Silcon Graphics, Inc.
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Christoph Hellwig.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Generic XTALK initialization code
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/smp.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_data/sgi-w1.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_data/xtalk-bridge.h>
158c2ecf20Sopenharmony_ci#include <asm/sn/addrs.h>
168c2ecf20Sopenharmony_ci#include <asm/sn/types.h>
178c2ecf20Sopenharmony_ci#include <asm/sn/klconfig.h>
188c2ecf20Sopenharmony_ci#include <asm/pci/bridge.h>
198c2ecf20Sopenharmony_ci#include <asm/xtalk/xtalk.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define XBOW_WIDGET_PART_NUM	0x0
238c2ecf20Sopenharmony_ci#define XXBOW_WIDGET_PART_NUM	0xd000	/* Xbow in Xbridge */
248c2ecf20Sopenharmony_ci#define BASE_XBOW_PORT		8     /* Lowest external port */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct xtalk_bridge_platform_data *bd;
298c2ecf20Sopenharmony_ci	struct sgi_w1_platform_data *wd;
308c2ecf20Sopenharmony_ci	struct platform_device *pdev_wd;
318c2ecf20Sopenharmony_ci	struct platform_device *pdev_bd;
328c2ecf20Sopenharmony_ci	struct resource w1_res;
338c2ecf20Sopenharmony_ci	unsigned long offset;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	offset = NODE_OFFSET(nasid);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	wd = kzalloc(sizeof(*wd), GFP_KERNEL);
388c2ecf20Sopenharmony_ci	if (!wd) {
398c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
408c2ecf20Sopenharmony_ci		return;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
448c2ecf20Sopenharmony_ci		 offset + (widget << SWIN_SIZE_BITS));
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	memset(&w1_res, 0, sizeof(w1_res));
478c2ecf20Sopenharmony_ci	w1_res.start = offset + (widget << SWIN_SIZE_BITS) +
488c2ecf20Sopenharmony_ci				offsetof(struct bridge_regs, b_nic);
498c2ecf20Sopenharmony_ci	w1_res.end = w1_res.start + 3;
508c2ecf20Sopenharmony_ci	w1_res.flags = IORESOURCE_MEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	pdev_wd = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
538c2ecf20Sopenharmony_ci	if (!pdev_wd) {
548c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
558c2ecf20Sopenharmony_ci		goto err_kfree_wd;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	if (platform_device_add_resources(pdev_wd, &w1_res, 1)) {
588c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge failed to add platform resources.\n", nasid, widget);
598c2ecf20Sopenharmony_ci		goto err_put_pdev_wd;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	if (platform_device_add_data(pdev_wd, wd, sizeof(*wd))) {
628c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
638c2ecf20Sopenharmony_ci		goto err_put_pdev_wd;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	if (platform_device_add(pdev_wd)) {
668c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
678c2ecf20Sopenharmony_ci		goto err_put_pdev_wd;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	/* platform_device_add_data() duplicates the data */
708c2ecf20Sopenharmony_ci	kfree(wd);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
738c2ecf20Sopenharmony_ci	if (!bd) {
748c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
758c2ecf20Sopenharmony_ci		goto err_unregister_pdev_wd;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	pdev_bd = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
788c2ecf20Sopenharmony_ci	if (!pdev_bd) {
798c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
808c2ecf20Sopenharmony_ci		goto err_kfree_bd;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
858c2ecf20Sopenharmony_ci	bd->intr_addr	= BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
868c2ecf20Sopenharmony_ci	bd->nasid	= nasid;
878c2ecf20Sopenharmony_ci	bd->masterwid	= masterwid;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	bd->mem.name	= "Bridge PCI MEM";
908c2ecf20Sopenharmony_ci	bd->mem.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
918c2ecf20Sopenharmony_ci	bd->mem.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
928c2ecf20Sopenharmony_ci	bd->mem.flags	= IORESOURCE_MEM;
938c2ecf20Sopenharmony_ci	bd->mem_offset	= offset;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	bd->io.name	= "Bridge PCI IO";
968c2ecf20Sopenharmony_ci	bd->io.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
978c2ecf20Sopenharmony_ci	bd->io.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
988c2ecf20Sopenharmony_ci	bd->io.flags	= IORESOURCE_IO;
998c2ecf20Sopenharmony_ci	bd->io_offset	= offset;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (platform_device_add_data(pdev_bd, bd, sizeof(*bd))) {
1028c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge failed to add platform data.\n", nasid, widget);
1038c2ecf20Sopenharmony_ci		goto err_put_pdev_bd;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	if (platform_device_add(pdev_bd)) {
1068c2ecf20Sopenharmony_ci		pr_warn("xtalk:n%d/%x bridge failed to add platform device.\n", nasid, widget);
1078c2ecf20Sopenharmony_ci		goto err_put_pdev_bd;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	/* platform_device_add_data() duplicates the data */
1108c2ecf20Sopenharmony_ci	kfree(bd);
1118c2ecf20Sopenharmony_ci	pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget);
1128c2ecf20Sopenharmony_ci	return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cierr_put_pdev_bd:
1158c2ecf20Sopenharmony_ci	platform_device_put(pdev_bd);
1168c2ecf20Sopenharmony_cierr_kfree_bd:
1178c2ecf20Sopenharmony_ci	kfree(bd);
1188c2ecf20Sopenharmony_cierr_unregister_pdev_wd:
1198c2ecf20Sopenharmony_ci	platform_device_unregister(pdev_wd);
1208c2ecf20Sopenharmony_ci	return;
1218c2ecf20Sopenharmony_cierr_put_pdev_wd:
1228c2ecf20Sopenharmony_ci	platform_device_put(pdev_wd);
1238c2ecf20Sopenharmony_cierr_kfree_wd:
1248c2ecf20Sopenharmony_ci	kfree(wd);
1258c2ecf20Sopenharmony_ci	return;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int probe_one_port(nasid_t nasid, int widget, int masterwid)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	widgetreg_t		widget_id;
1318c2ecf20Sopenharmony_ci	xwidget_part_num_t	partnum;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	widget_id = *(volatile widgetreg_t *)
1348c2ecf20Sopenharmony_ci		(RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID);
1358c2ecf20Sopenharmony_ci	partnum = XWIDGET_PART_NUM(widget_id);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	switch (partnum) {
1388c2ecf20Sopenharmony_ci	case BRIDGE_WIDGET_PART_NUM:
1398c2ecf20Sopenharmony_ci	case XBRIDGE_WIDGET_PART_NUM:
1408c2ecf20Sopenharmony_ci		bridge_platform_create(nasid, widget, masterwid);
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci	default:
1438c2ecf20Sopenharmony_ci		pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
1448c2ecf20Sopenharmony_ci			nasid, widget, partnum);
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int xbow_probe(nasid_t nasid)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	lboard_t *brd;
1548c2ecf20Sopenharmony_ci	klxbow_t *xbow_p;
1558c2ecf20Sopenharmony_ci	unsigned masterwid, i;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/*
1588c2ecf20Sopenharmony_ci	 * found xbow, so may have multiple bridges
1598c2ecf20Sopenharmony_ci	 * need to probe xbow
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
1628c2ecf20Sopenharmony_ci	if (!brd)
1638c2ecf20Sopenharmony_ci		return -ENODEV;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW);
1668c2ecf20Sopenharmony_ci	if (!xbow_p)
1678c2ecf20Sopenharmony_ci		return -ENODEV;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * Okay, here's a xbow. Let's arbitrate and find
1718c2ecf20Sopenharmony_ci	 * out if we should initialize it. Set enabled
1728c2ecf20Sopenharmony_ci	 * hub connected at highest or lowest widget as
1738c2ecf20Sopenharmony_ci	 * master.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci#ifdef WIDGET_A
1768c2ecf20Sopenharmony_ci	i = HUB_WIDGET_ID_MAX + 1;
1778c2ecf20Sopenharmony_ci	do {
1788c2ecf20Sopenharmony_ci		i--;
1798c2ecf20Sopenharmony_ci	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
1808c2ecf20Sopenharmony_ci		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
1818c2ecf20Sopenharmony_ci#else
1828c2ecf20Sopenharmony_ci	i = HUB_WIDGET_ID_MIN - 1;
1838c2ecf20Sopenharmony_ci	do {
1848c2ecf20Sopenharmony_ci		i++;
1858c2ecf20Sopenharmony_ci	} while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) ||
1868c2ecf20Sopenharmony_ci		 (!XBOW_PORT_IS_ENABLED(xbow_p, i)));
1878c2ecf20Sopenharmony_ci#endif
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	masterwid = i;
1908c2ecf20Sopenharmony_ci	if (nasid != XBOW_PORT_NASID(xbow_p, i))
1918c2ecf20Sopenharmony_ci		return 1;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) {
1948c2ecf20Sopenharmony_ci		if (XBOW_PORT_IS_ENABLED(xbow_p, i) &&
1958c2ecf20Sopenharmony_ci		    XBOW_PORT_TYPE_IO(xbow_p, i))
1968c2ecf20Sopenharmony_ci			probe_one_port(nasid, i, masterwid);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void xtalk_probe_node(nasid_t nasid)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	volatile u64		hubreg;
2058c2ecf20Sopenharmony_ci	xwidget_part_num_t	partnum;
2068c2ecf20Sopenharmony_ci	widgetreg_t		widget_id;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* check whether the link is up */
2118c2ecf20Sopenharmony_ci	if (!(hubreg & IIO_LLP_CSR_IS_UP))
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	widget_id = *(volatile widgetreg_t *)
2158c2ecf20Sopenharmony_ci		       (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID);
2168c2ecf20Sopenharmony_ci	partnum = XWIDGET_PART_NUM(widget_id);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	switch (partnum) {
2198c2ecf20Sopenharmony_ci	case BRIDGE_WIDGET_PART_NUM:
2208c2ecf20Sopenharmony_ci		bridge_platform_create(nasid, 0x8, 0xa);
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci	case XBOW_WIDGET_PART_NUM:
2238c2ecf20Sopenharmony_ci	case XXBOW_WIDGET_PART_NUM:
2248c2ecf20Sopenharmony_ci		pr_info("xtalk:n%d/0 xbow widget\n", nasid);
2258c2ecf20Sopenharmony_ci		xbow_probe(nasid);
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci	default:
2288c2ecf20Sopenharmony_ci		pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum);
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int __init xtalk_init(void)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	nasid_t nasid;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	for_each_online_node(nasid)
2388c2ecf20Sopenharmony_ci		xtalk_probe_node(nasid);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ciarch_initcall(xtalk_init);
243