Thomas Bogendoerfer | 7505576 | 2019-10-24 12:18:29 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support. |
| 4 | * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org> |
| 5 | * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de> |
| 6 | * Copyright (C) 2007, 2014-2016 Joshua Kinard <kumba@gentoo.org> |
| 7 | */ |
| 8 | |
| 9 | #include <linux/init.h> |
| 10 | #include <linux/kernel.h> |
| 11 | #include <linux/platform_device.h> |
| 12 | #include <linux/platform_data/sgi-w1.h> |
| 13 | #include <linux/platform_data/xtalk-bridge.h> |
| 14 | |
| 15 | #include <asm/xtalk/xwidget.h> |
| 16 | #include <asm/pci/bridge.h> |
| 17 | |
| 18 | #define IP30_SWIN_BASE(widget) \ |
| 19 | (0x0000000010000000 | (((unsigned long)(widget)) << 24)) |
| 20 | |
| 21 | #define IP30_RAW_SWIN_BASE(widget) (IO_BASE + IP30_SWIN_BASE(widget)) |
| 22 | |
| 23 | #define IP30_SWIN_SIZE (1 << 24) |
| 24 | |
| 25 | #define IP30_WIDGET_XBOW _AC(0x0, UL) /* XBow is always 0 */ |
| 26 | #define IP30_WIDGET_HEART _AC(0x8, UL) /* HEART is always 8 */ |
| 27 | #define IP30_WIDGET_PCI_BASE _AC(0xf, UL) /* BaseIO PCI is always 15 */ |
| 28 | |
| 29 | #define XTALK_NODEV 0xffffffff |
| 30 | |
| 31 | #define XBOW_REG_LINK_STAT_0 0x114 |
| 32 | #define XBOW_REG_LINK_BLK_SIZE 0x40 |
| 33 | #define XBOW_REG_LINK_ALIVE 0x80000000 |
| 34 | |
| 35 | #define HEART_INTR_ADDR 0x00000080 |
| 36 | |
| 37 | #define xtalk_read __raw_readl |
| 38 | |
| 39 | static void bridge_platform_create(int widget, int masterwid) |
| 40 | { |
| 41 | struct xtalk_bridge_platform_data *bd; |
| 42 | struct sgi_w1_platform_data *wd; |
| 43 | struct platform_device *pdev; |
| 44 | struct resource w1_res; |
| 45 | |
| 46 | wd = kzalloc(sizeof(*wd), GFP_KERNEL); |
| 47 | if (!wd) |
| 48 | goto no_mem; |
| 49 | |
| 50 | snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", |
| 51 | IP30_SWIN_BASE(widget)); |
| 52 | |
| 53 | memset(&w1_res, 0, sizeof(w1_res)); |
| 54 | w1_res.start = IP30_SWIN_BASE(widget) + |
| 55 | offsetof(struct bridge_regs, b_nic); |
| 56 | w1_res.end = w1_res.start + 3; |
| 57 | w1_res.flags = IORESOURCE_MEM; |
| 58 | |
| 59 | pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); |
| 60 | if (!pdev) { |
| 61 | kfree(wd); |
| 62 | goto no_mem; |
| 63 | } |
| 64 | platform_device_add_resources(pdev, &w1_res, 1); |
| 65 | platform_device_add_data(pdev, wd, sizeof(*wd)); |
| 66 | platform_device_add(pdev); |
| 67 | |
| 68 | bd = kzalloc(sizeof(*bd), GFP_KERNEL); |
| 69 | if (!bd) |
| 70 | goto no_mem; |
| 71 | pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); |
| 72 | if (!pdev) { |
| 73 | kfree(bd); |
| 74 | goto no_mem; |
| 75 | } |
| 76 | |
| 77 | bd->bridge_addr = IP30_RAW_SWIN_BASE(widget); |
| 78 | bd->intr_addr = HEART_INTR_ADDR; |
| 79 | bd->nasid = 0; |
| 80 | bd->masterwid = masterwid; |
| 81 | |
| 82 | bd->mem.name = "Bridge PCI MEM"; |
| 83 | bd->mem.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; |
| 84 | bd->mem.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; |
| 85 | bd->mem.flags = IORESOURCE_MEM; |
| 86 | bd->mem_offset = IP30_SWIN_BASE(widget); |
| 87 | |
| 88 | bd->io.name = "Bridge PCI IO"; |
| 89 | bd->io.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; |
| 90 | bd->io.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; |
| 91 | bd->io.flags = IORESOURCE_IO; |
| 92 | bd->io_offset = IP30_SWIN_BASE(widget); |
| 93 | |
| 94 | platform_device_add_data(pdev, bd, sizeof(*bd)); |
| 95 | platform_device_add(pdev); |
| 96 | pr_info("xtalk:%x bridge widget\n", widget); |
| 97 | return; |
| 98 | |
| 99 | no_mem: |
| 100 | pr_warn("xtalk:%x bridge create out of memory\n", widget); |
| 101 | } |
| 102 | |
| 103 | static unsigned int __init xbow_widget_active(s8 wid) |
| 104 | { |
| 105 | unsigned int link_stat; |
| 106 | |
| 107 | link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) + |
| 108 | XBOW_REG_LINK_STAT_0 + |
| 109 | XBOW_REG_LINK_BLK_SIZE * |
| 110 | (wid - 8))); |
| 111 | |
| 112 | return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0; |
| 113 | } |
| 114 | |
| 115 | static void __init xtalk_init_widget(s8 wid, s8 masterwid) |
| 116 | { |
| 117 | xwidget_part_num_t partnum; |
| 118 | widgetreg_t widget_id; |
| 119 | |
| 120 | if (!xbow_widget_active(wid)) |
| 121 | return; |
| 122 | |
| 123 | widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID)); |
| 124 | |
| 125 | partnum = XWIDGET_PART_NUM(widget_id); |
| 126 | |
| 127 | switch (partnum) { |
| 128 | case BRIDGE_WIDGET_PART_NUM: |
| 129 | case XBRIDGE_WIDGET_PART_NUM: |
| 130 | bridge_platform_create(wid, masterwid); |
| 131 | break; |
| 132 | default: |
| 133 | pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum); |
| 134 | break; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | static int __init ip30_xtalk_init(void) |
| 139 | { |
| 140 | int i; |
| 141 | |
| 142 | /* |
| 143 | * Walk widget IDs backwards so that BaseIO is probed first. This |
| 144 | * ensures that the BaseIO IOC3 is always detected as eth0. |
| 145 | */ |
| 146 | for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--) |
| 147 | xtalk_init_widget(i, IP30_WIDGET_HEART); |
| 148 | |
| 149 | return 0; |
| 150 | } |
| 151 | |
| 152 | arch_initcall(ip30_xtalk_init); |