1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) 4 * Copyright (C) 1999, 2000 Silcon Graphics, Inc. 5 * Copyright (C) 2004 Christoph Hellwig. 6 * 7 * Generic XTALK initialization code 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/smp.h> 12 #include <linux/platform_device.h> 13 #include <linux/platform_data/sgi-w1.h> 14 #include <linux/platform_data/xtalk-bridge.h> 15 #include <asm/sn/addrs.h> 16 #include <asm/sn/types.h> 17 #include <asm/sn/klconfig.h> 18 #include <asm/pci/bridge.h> 19 #include <asm/xtalk/xtalk.h> 20 21 22 #define XBOW_WIDGET_PART_NUM 0x0 23 #define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */ 24 #define BASE_XBOW_PORT 8 /* Lowest external port */ 25 26 static void bridge_platform_create(nasid_t nasid, int widget, int masterwid) 27 { 28 struct xtalk_bridge_platform_data *bd; 29 struct sgi_w1_platform_data *wd; 30 struct platform_device *pdev; 31 struct resource w1_res; 32 unsigned long offset; 33 34 offset = NODE_OFFSET(nasid); 35 36 wd = kzalloc(sizeof(*wd), GFP_KERNEL); 37 if (!wd) 38 goto no_mem; 39 40 snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", 41 offset + (widget << SWIN_SIZE_BITS)); 42 43 memset(&w1_res, 0, sizeof(w1_res)); 44 w1_res.start = offset + (widget << SWIN_SIZE_BITS) + 45 offsetof(struct bridge_regs, b_nic); 46 w1_res.end = w1_res.start + 3; 47 w1_res.flags = IORESOURCE_MEM; 48 49 pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); 50 if (!pdev) { 51 kfree(wd); 52 goto no_mem; 53 } 54 platform_device_add_resources(pdev, &w1_res, 1); 55 platform_device_add_data(pdev, wd, sizeof(*wd)); 56 /* platform_device_add_data() duplicates the data */ 57 kfree(wd); 58 platform_device_add(pdev); 59 60 bd = kzalloc(sizeof(*bd), GFP_KERNEL); 61 if (!bd) 62 goto no_mem; 63 pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); 64 if (!pdev) { 65 kfree(bd); 66 goto no_mem; 67 } 68 69 70 bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget); 71 bd->intr_addr = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD; 72 bd->nasid = nasid; 73 bd->masterwid = masterwid; 74 75 bd->mem.name = "Bridge PCI MEM"; 76 bd->mem.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0; 77 bd->mem.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1; 78 bd->mem.flags = IORESOURCE_MEM; 79 bd->mem_offset = offset; 80 81 bd->io.name = "Bridge PCI IO"; 82 bd->io.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0; 83 bd->io.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1; 84 bd->io.flags = IORESOURCE_IO; 85 bd->io_offset = offset; 86 87 platform_device_add_data(pdev, bd, sizeof(*bd)); 88 /* platform_device_add_data() duplicates the data */ 89 kfree(bd); 90 platform_device_add(pdev); 91 pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget); 92 return; 93 94 no_mem: 95 pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget); 96 } 97 98 static int probe_one_port(nasid_t nasid, int widget, int masterwid) 99 { 100 widgetreg_t widget_id; 101 xwidget_part_num_t partnum; 102 103 widget_id = *(volatile widgetreg_t *) 104 (RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID); 105 partnum = XWIDGET_PART_NUM(widget_id); 106 107 switch (partnum) { 108 case BRIDGE_WIDGET_PART_NUM: 109 case XBRIDGE_WIDGET_PART_NUM: 110 bridge_platform_create(nasid, widget, masterwid); 111 break; 112 default: 113 pr_info("xtalk:n%d/%d unknown widget (0x%x)\n", 114 nasid, widget, partnum); 115 break; 116 } 117 118 return 0; 119 } 120 121 static int xbow_probe(nasid_t nasid) 122 { 123 lboard_t *brd; 124 klxbow_t *xbow_p; 125 unsigned masterwid, i; 126 127 /* 128 * found xbow, so may have multiple bridges 129 * need to probe xbow 130 */ 131 brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8); 132 if (!brd) 133 return -ENODEV; 134 135 xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW); 136 if (!xbow_p) 137 return -ENODEV; 138 139 /* 140 * Okay, here's a xbow. Let's arbitrate and find 141 * out if we should initialize it. Set enabled 142 * hub connected at highest or lowest widget as 143 * master. 144 */ 145 #ifdef WIDGET_A 146 i = HUB_WIDGET_ID_MAX + 1; 147 do { 148 i--; 149 } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || 150 (!XBOW_PORT_IS_ENABLED(xbow_p, i))); 151 #else 152 i = HUB_WIDGET_ID_MIN - 1; 153 do { 154 i++; 155 } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || 156 (!XBOW_PORT_IS_ENABLED(xbow_p, i))); 157 #endif 158 159 masterwid = i; 160 if (nasid != XBOW_PORT_NASID(xbow_p, i)) 161 return 1; 162 163 for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) { 164 if (XBOW_PORT_IS_ENABLED(xbow_p, i) && 165 XBOW_PORT_TYPE_IO(xbow_p, i)) 166 probe_one_port(nasid, i, masterwid); 167 } 168 169 return 0; 170 } 171 172 static void xtalk_probe_node(nasid_t nasid) 173 { 174 volatile u64 hubreg; 175 xwidget_part_num_t partnum; 176 widgetreg_t widget_id; 177 178 hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR); 179 180 /* check whether the link is up */ 181 if (!(hubreg & IIO_LLP_CSR_IS_UP)) 182 return; 183 184 widget_id = *(volatile widgetreg_t *) 185 (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); 186 partnum = XWIDGET_PART_NUM(widget_id); 187 188 switch (partnum) { 189 case BRIDGE_WIDGET_PART_NUM: 190 bridge_platform_create(nasid, 0x8, 0xa); 191 break; 192 case XBOW_WIDGET_PART_NUM: 193 case XXBOW_WIDGET_PART_NUM: 194 pr_info("xtalk:n%d/0 xbow widget\n", nasid); 195 xbow_probe(nasid); 196 break; 197 default: 198 pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum); 199 break; 200 } 201 } 202 203 static int __init xtalk_init(void) 204 { 205 nasid_t nasid; 206 207 for_each_online_node(nasid) 208 xtalk_probe_node(nasid); 209 210 return 0; 211 } 212 arch_initcall(xtalk_init); 213