11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) 31da177e4SLinus Torvalds * Copyright (C) 1999, 2000 Silcon Graphics, Inc. 41da177e4SLinus Torvalds * Copyright (C) 2004 Christoph Hellwig. 51da177e4SLinus Torvalds * Released under GPL v2. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Generic XTALK initialization code 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #include <linux/kernel.h> 11631330f5SRalf Baechle #include <linux/smp.h> 12*a57140e9SThomas Bogendoerfer #include <linux/platform_device.h> 13*a57140e9SThomas Bogendoerfer #include <linux/platform_data/xtalk-bridge.h> 14*a57140e9SThomas Bogendoerfer #include <asm/sn/addrs.h> 151da177e4SLinus Torvalds #include <asm/sn/types.h> 161da177e4SLinus Torvalds #include <asm/sn/klconfig.h> 171da177e4SLinus Torvalds #include <asm/sn/hub.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 26*a57140e9SThomas Bogendoerfer static void bridge_platform_create(nasid_t nasid, int widget, int masterwid) 27*a57140e9SThomas Bogendoerfer { 28*a57140e9SThomas Bogendoerfer struct xtalk_bridge_platform_data *bd; 29*a57140e9SThomas Bogendoerfer struct platform_device *pdev; 30*a57140e9SThomas Bogendoerfer unsigned long offset; 31*a57140e9SThomas Bogendoerfer 32*a57140e9SThomas Bogendoerfer bd = kzalloc(sizeof(*bd), GFP_KERNEL); 33*a57140e9SThomas Bogendoerfer if (!bd) 34*a57140e9SThomas Bogendoerfer goto no_mem; 35*a57140e9SThomas Bogendoerfer pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); 36*a57140e9SThomas Bogendoerfer if (!pdev) { 37*a57140e9SThomas Bogendoerfer kfree(bd); 38*a57140e9SThomas Bogendoerfer goto no_mem; 39*a57140e9SThomas Bogendoerfer } 40*a57140e9SThomas Bogendoerfer 41*a57140e9SThomas Bogendoerfer offset = NODE_OFFSET(nasid); 42*a57140e9SThomas Bogendoerfer 43*a57140e9SThomas Bogendoerfer bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget); 44*a57140e9SThomas Bogendoerfer bd->intr_addr = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD; 45*a57140e9SThomas Bogendoerfer bd->nasid = nasid; 46*a57140e9SThomas Bogendoerfer bd->masterwid = masterwid; 47*a57140e9SThomas Bogendoerfer 48*a57140e9SThomas Bogendoerfer bd->mem.name = "Bridge PCI MEM"; 49*a57140e9SThomas Bogendoerfer bd->mem.start = offset + (widget << SWIN_SIZE_BITS); 50*a57140e9SThomas Bogendoerfer bd->mem.end = bd->mem.start + SWIN_SIZE - 1; 51*a57140e9SThomas Bogendoerfer bd->mem.flags = IORESOURCE_MEM; 52*a57140e9SThomas Bogendoerfer bd->mem_offset = offset; 53*a57140e9SThomas Bogendoerfer 54*a57140e9SThomas Bogendoerfer bd->io.name = "Bridge PCI IO"; 55*a57140e9SThomas Bogendoerfer bd->io.start = offset + (widget << SWIN_SIZE_BITS); 56*a57140e9SThomas Bogendoerfer bd->io.end = bd->io.start + SWIN_SIZE - 1; 57*a57140e9SThomas Bogendoerfer bd->io.flags = IORESOURCE_IO; 58*a57140e9SThomas Bogendoerfer bd->io_offset = offset; 59*a57140e9SThomas Bogendoerfer 60*a57140e9SThomas Bogendoerfer platform_device_add_data(pdev, bd, sizeof(*bd)); 61*a57140e9SThomas Bogendoerfer platform_device_add(pdev); 62*a57140e9SThomas Bogendoerfer pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget); 63*a57140e9SThomas Bogendoerfer return; 64*a57140e9SThomas Bogendoerfer 65*a57140e9SThomas Bogendoerfer no_mem: 66*a57140e9SThomas Bogendoerfer pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget); 67*a57140e9SThomas Bogendoerfer } 681da177e4SLinus Torvalds 69078a55fcSPaul Gortmaker static int probe_one_port(nasid_t nasid, int widget, int masterwid) 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds widgetreg_t widget_id; 721da177e4SLinus Torvalds xwidget_part_num_t partnum; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds widget_id = *(volatile widgetreg_t *) 751da177e4SLinus Torvalds (RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID); 761da177e4SLinus Torvalds partnum = XWIDGET_PART_NUM(widget_id); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds switch (partnum) { 791da177e4SLinus Torvalds case BRIDGE_WIDGET_PART_NUM: 801da177e4SLinus Torvalds case XBRIDGE_WIDGET_PART_NUM: 81*a57140e9SThomas Bogendoerfer bridge_platform_create(nasid, widget, masterwid); 821da177e4SLinus Torvalds break; 831da177e4SLinus Torvalds default: 841da177e4SLinus Torvalds break; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds return 0; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 90078a55fcSPaul Gortmaker static int xbow_probe(nasid_t nasid) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds lboard_t *brd; 931da177e4SLinus Torvalds klxbow_t *xbow_p; 941da177e4SLinus Torvalds unsigned masterwid, i; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /* 971da177e4SLinus Torvalds * found xbow, so may have multiple bridges 981da177e4SLinus Torvalds * need to probe xbow 991da177e4SLinus Torvalds */ 1001da177e4SLinus Torvalds brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8); 1011da177e4SLinus Torvalds if (!brd) 1021da177e4SLinus Torvalds return -ENODEV; 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW); 1051da177e4SLinus Torvalds if (!xbow_p) 1061da177e4SLinus Torvalds return -ENODEV; 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds /* 1094939788eSRalf Baechle * Okay, here's a xbow. Let's arbitrate and find 1101da177e4SLinus Torvalds * out if we should initialize it. Set enabled 1111da177e4SLinus Torvalds * hub connected at highest or lowest widget as 1121da177e4SLinus Torvalds * master. 1131da177e4SLinus Torvalds */ 1141da177e4SLinus Torvalds #ifdef WIDGET_A 1151da177e4SLinus Torvalds i = HUB_WIDGET_ID_MAX + 1; 1161da177e4SLinus Torvalds do { 1171da177e4SLinus Torvalds i--; 1181da177e4SLinus Torvalds } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || 1191da177e4SLinus Torvalds (!XBOW_PORT_IS_ENABLED(xbow_p, i))); 1201da177e4SLinus Torvalds #else 1211da177e4SLinus Torvalds i = HUB_WIDGET_ID_MIN - 1; 1221da177e4SLinus Torvalds do { 1231da177e4SLinus Torvalds i++; 1241da177e4SLinus Torvalds } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || 1251da177e4SLinus Torvalds (!XBOW_PORT_IS_ENABLED(xbow_p, i))); 1261da177e4SLinus Torvalds #endif 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds masterwid = i; 1291da177e4SLinus Torvalds if (nasid != XBOW_PORT_NASID(xbow_p, i)) 1301da177e4SLinus Torvalds return 1; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) { 1331da177e4SLinus Torvalds if (XBOW_PORT_IS_ENABLED(xbow_p, i) && 1341da177e4SLinus Torvalds XBOW_PORT_TYPE_IO(xbow_p, i)) 1351da177e4SLinus Torvalds probe_one_port(nasid, i, masterwid); 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds return 0; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1419707b7e6SThomas Bogendoerfer static void xtalk_probe_node(cnodeid_t nid) 1421da177e4SLinus Torvalds { 1431da177e4SLinus Torvalds volatile u64 hubreg; 1441da177e4SLinus Torvalds nasid_t nasid; 1451da177e4SLinus Torvalds xwidget_part_num_t partnum; 1461da177e4SLinus Torvalds widgetreg_t widget_id; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds nasid = COMPACT_TO_NASID_NODEID(nid); 1491da177e4SLinus Torvalds hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR); 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds /* check whether the link is up */ 1521da177e4SLinus Torvalds if (!(hubreg & IIO_LLP_CSR_IS_UP)) 1531da177e4SLinus Torvalds return; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds widget_id = *(volatile widgetreg_t *) 1561da177e4SLinus Torvalds (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); 1571da177e4SLinus Torvalds partnum = XWIDGET_PART_NUM(widget_id); 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds switch (partnum) { 1601da177e4SLinus Torvalds case BRIDGE_WIDGET_PART_NUM: 161*a57140e9SThomas Bogendoerfer bridge_platform_create(nasid, 0x8, 0xa); 1621da177e4SLinus Torvalds break; 1631da177e4SLinus Torvalds case XBOW_WIDGET_PART_NUM: 1641da177e4SLinus Torvalds case XXBOW_WIDGET_PART_NUM: 165*a57140e9SThomas Bogendoerfer pr_info("xtalk:n%d/0 xbow widget\n", nasid); 1661da177e4SLinus Torvalds xbow_probe(nasid); 1671da177e4SLinus Torvalds break; 1681da177e4SLinus Torvalds default: 169*a57140e9SThomas Bogendoerfer pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum); 1701da177e4SLinus Torvalds break; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds } 1739707b7e6SThomas Bogendoerfer 1749707b7e6SThomas Bogendoerfer static int __init xtalk_init(void) 1759707b7e6SThomas Bogendoerfer { 1769707b7e6SThomas Bogendoerfer cnodeid_t cnode; 1779707b7e6SThomas Bogendoerfer 1789707b7e6SThomas Bogendoerfer for_each_online_node(cnode) 1799707b7e6SThomas Bogendoerfer xtalk_probe_node(cnode); 1809707b7e6SThomas Bogendoerfer 1819707b7e6SThomas Bogendoerfer return 0; 1829707b7e6SThomas Bogendoerfer } 1839707b7e6SThomas Bogendoerfer arch_initcall(xtalk_init); 184