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