|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) | 
|  | * Copyright (C) 1999, 2000 Silcon Graphics, Inc. | 
|  | * Copyright (C) 2004 Christoph Hellwig. | 
|  | * | 
|  | * Generic XTALK initialization code | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/smp.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/platform_data/sgi-w1.h> | 
|  | #include <linux/platform_data/xtalk-bridge.h> | 
|  | #include <asm/sn/addrs.h> | 
|  | #include <asm/sn/types.h> | 
|  | #include <asm/sn/klconfig.h> | 
|  | #include <asm/pci/bridge.h> | 
|  | #include <asm/xtalk/xtalk.h> | 
|  |  | 
|  |  | 
|  | #define XBOW_WIDGET_PART_NUM	0x0 | 
|  | #define XXBOW_WIDGET_PART_NUM	0xd000	/* Xbow in Xbridge */ | 
|  | #define BASE_XBOW_PORT		8     /* Lowest external port */ | 
|  |  | 
|  | static void bridge_platform_create(nasid_t nasid, int widget, int masterwid) | 
|  | { | 
|  | struct xtalk_bridge_platform_data *bd; | 
|  | struct sgi_w1_platform_data *wd; | 
|  | struct platform_device *pdev; | 
|  | struct resource w1_res; | 
|  | unsigned long offset; | 
|  |  | 
|  | offset = NODE_OFFSET(nasid); | 
|  |  | 
|  | wd = kzalloc(sizeof(*wd), GFP_KERNEL); | 
|  | if (!wd) | 
|  | goto no_mem; | 
|  |  | 
|  | snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", | 
|  | offset + (widget << SWIN_SIZE_BITS)); | 
|  |  | 
|  | memset(&w1_res, 0, sizeof(w1_res)); | 
|  | w1_res.start = offset + (widget << SWIN_SIZE_BITS) + | 
|  | offsetof(struct bridge_regs, b_nic); | 
|  | w1_res.end = w1_res.start + 3; | 
|  | w1_res.flags = IORESOURCE_MEM; | 
|  |  | 
|  | pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); | 
|  | if (!pdev) { | 
|  | kfree(wd); | 
|  | goto no_mem; | 
|  | } | 
|  | platform_device_add_resources(pdev, &w1_res, 1); | 
|  | platform_device_add_data(pdev, wd, sizeof(*wd)); | 
|  | /* platform_device_add_data() duplicates the data */ | 
|  | kfree(wd); | 
|  | platform_device_add(pdev); | 
|  |  | 
|  | bd = kzalloc(sizeof(*bd), GFP_KERNEL); | 
|  | if (!bd) | 
|  | goto no_mem; | 
|  | pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); | 
|  | if (!pdev) { | 
|  | kfree(bd); | 
|  | goto no_mem; | 
|  | } | 
|  |  | 
|  |  | 
|  | bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget); | 
|  | bd->intr_addr	= BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD; | 
|  | bd->nasid	= nasid; | 
|  | bd->masterwid	= masterwid; | 
|  |  | 
|  | bd->mem.name	= "Bridge PCI MEM"; | 
|  | bd->mem.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0; | 
|  | bd->mem.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1; | 
|  | bd->mem.flags	= IORESOURCE_MEM; | 
|  | bd->mem_offset	= offset; | 
|  |  | 
|  | bd->io.name	= "Bridge PCI IO"; | 
|  | bd->io.start	= offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0; | 
|  | bd->io.end	= offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1; | 
|  | bd->io.flags	= IORESOURCE_IO; | 
|  | bd->io_offset	= offset; | 
|  |  | 
|  | platform_device_add_data(pdev, bd, sizeof(*bd)); | 
|  | /* platform_device_add_data() duplicates the data */ | 
|  | kfree(bd); | 
|  | platform_device_add(pdev); | 
|  | pr_info("xtalk:n%d/%x bridge widget\n", nasid, widget); | 
|  | return; | 
|  |  | 
|  | no_mem: | 
|  | pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget); | 
|  | } | 
|  |  | 
|  | static int probe_one_port(nasid_t nasid, int widget, int masterwid) | 
|  | { | 
|  | widgetreg_t		widget_id; | 
|  | xwidget_part_num_t	partnum; | 
|  |  | 
|  | widget_id = *(volatile widgetreg_t *) | 
|  | (RAW_NODE_SWIN_BASE(nasid, widget) + WIDGET_ID); | 
|  | partnum = XWIDGET_PART_NUM(widget_id); | 
|  |  | 
|  | switch (partnum) { | 
|  | case BRIDGE_WIDGET_PART_NUM: | 
|  | case XBRIDGE_WIDGET_PART_NUM: | 
|  | bridge_platform_create(nasid, widget, masterwid); | 
|  | break; | 
|  | default: | 
|  | pr_info("xtalk:n%d/%d unknown widget (0x%x)\n", | 
|  | nasid, widget, partnum); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int xbow_probe(nasid_t nasid) | 
|  | { | 
|  | lboard_t *brd; | 
|  | klxbow_t *xbow_p; | 
|  | unsigned masterwid, i; | 
|  |  | 
|  | /* | 
|  | * found xbow, so may have multiple bridges | 
|  | * need to probe xbow | 
|  | */ | 
|  | brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8); | 
|  | if (!brd) | 
|  | return -ENODEV; | 
|  |  | 
|  | xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW); | 
|  | if (!xbow_p) | 
|  | return -ENODEV; | 
|  |  | 
|  | /* | 
|  | * Okay, here's a xbow. Let's arbitrate and find | 
|  | * out if we should initialize it. Set enabled | 
|  | * hub connected at highest or lowest widget as | 
|  | * master. | 
|  | */ | 
|  | #ifdef WIDGET_A | 
|  | i = HUB_WIDGET_ID_MAX + 1; | 
|  | do { | 
|  | i--; | 
|  | } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || | 
|  | (!XBOW_PORT_IS_ENABLED(xbow_p, i))); | 
|  | #else | 
|  | i = HUB_WIDGET_ID_MIN - 1; | 
|  | do { | 
|  | i++; | 
|  | } while ((!XBOW_PORT_TYPE_HUB(xbow_p, i)) || | 
|  | (!XBOW_PORT_IS_ENABLED(xbow_p, i))); | 
|  | #endif | 
|  |  | 
|  | masterwid = i; | 
|  | if (nasid != XBOW_PORT_NASID(xbow_p, i)) | 
|  | return 1; | 
|  |  | 
|  | for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++) { | 
|  | if (XBOW_PORT_IS_ENABLED(xbow_p, i) && | 
|  | XBOW_PORT_TYPE_IO(xbow_p, i)) | 
|  | probe_one_port(nasid, i, masterwid); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void xtalk_probe_node(nasid_t nasid) | 
|  | { | 
|  | volatile u64		hubreg; | 
|  | xwidget_part_num_t	partnum; | 
|  | widgetreg_t		widget_id; | 
|  |  | 
|  | hubreg = REMOTE_HUB_L(nasid, IIO_LLP_CSR); | 
|  |  | 
|  | /* check whether the link is up */ | 
|  | if (!(hubreg & IIO_LLP_CSR_IS_UP)) | 
|  | return; | 
|  |  | 
|  | widget_id = *(volatile widgetreg_t *) | 
|  | (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); | 
|  | partnum = XWIDGET_PART_NUM(widget_id); | 
|  |  | 
|  | switch (partnum) { | 
|  | case BRIDGE_WIDGET_PART_NUM: | 
|  | bridge_platform_create(nasid, 0x8, 0xa); | 
|  | break; | 
|  | case XBOW_WIDGET_PART_NUM: | 
|  | case XXBOW_WIDGET_PART_NUM: | 
|  | pr_info("xtalk:n%d/0 xbow widget\n", nasid); | 
|  | xbow_probe(nasid); | 
|  | break; | 
|  | default: | 
|  | pr_info("xtalk:n%d/0 unknown widget (0x%x)\n", nasid, partnum); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int __init xtalk_init(void) | 
|  | { | 
|  | nasid_t nasid; | 
|  |  | 
|  | for_each_online_node(nasid) | 
|  | xtalk_probe_node(nasid); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | arch_initcall(xtalk_init); |