forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
4.9 KiB
208 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Copyright (C) 1999, 2000 Ralf Baechle ([email protected]) |
|
* 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(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(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);
|
|
|