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