18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support. 48c2ecf20Sopenharmony_ci * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org> 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de> 68c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2014-2016 Joshua Kinard <kumba@gentoo.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_data/sgi-w1.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_data/xtalk-bridge.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/xtalk/xwidget.h> 168c2ecf20Sopenharmony_ci#include <asm/pci/bridge.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define IP30_SWIN_BASE(widget) \ 198c2ecf20Sopenharmony_ci (0x0000000010000000 | (((unsigned long)(widget)) << 24)) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define IP30_RAW_SWIN_BASE(widget) (IO_BASE + IP30_SWIN_BASE(widget)) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define IP30_SWIN_SIZE (1 << 24) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define IP30_WIDGET_XBOW _AC(0x0, UL) /* XBow is always 0 */ 268c2ecf20Sopenharmony_ci#define IP30_WIDGET_HEART _AC(0x8, UL) /* HEART is always 8 */ 278c2ecf20Sopenharmony_ci#define IP30_WIDGET_PCI_BASE _AC(0xf, UL) /* BaseIO PCI is always 15 */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define XTALK_NODEV 0xffffffff 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define XBOW_REG_LINK_STAT_0 0x114 328c2ecf20Sopenharmony_ci#define XBOW_REG_LINK_BLK_SIZE 0x40 338c2ecf20Sopenharmony_ci#define XBOW_REG_LINK_ALIVE 0x80000000 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define HEART_INTR_ADDR 0x00000080 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define xtalk_read __raw_readl 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void bridge_platform_create(int widget, int masterwid) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct xtalk_bridge_platform_data *bd; 428c2ecf20Sopenharmony_ci struct sgi_w1_platform_data *wd; 438c2ecf20Sopenharmony_ci struct platform_device *pdev; 448c2ecf20Sopenharmony_ci struct resource w1_res; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci wd = kzalloc(sizeof(*wd), GFP_KERNEL); 478c2ecf20Sopenharmony_ci if (!wd) 488c2ecf20Sopenharmony_ci goto no_mem; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", 518c2ecf20Sopenharmony_ci IP30_SWIN_BASE(widget)); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci memset(&w1_res, 0, sizeof(w1_res)); 548c2ecf20Sopenharmony_ci w1_res.start = IP30_SWIN_BASE(widget) + 558c2ecf20Sopenharmony_ci offsetof(struct bridge_regs, b_nic); 568c2ecf20Sopenharmony_ci w1_res.end = w1_res.start + 3; 578c2ecf20Sopenharmony_ci w1_res.flags = IORESOURCE_MEM; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); 608c2ecf20Sopenharmony_ci if (!pdev) { 618c2ecf20Sopenharmony_ci kfree(wd); 628c2ecf20Sopenharmony_ci goto no_mem; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci platform_device_add_resources(pdev, &w1_res, 1); 658c2ecf20Sopenharmony_ci platform_device_add_data(pdev, wd, sizeof(*wd)); 668c2ecf20Sopenharmony_ci platform_device_add(pdev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci bd = kzalloc(sizeof(*bd), GFP_KERNEL); 698c2ecf20Sopenharmony_ci if (!bd) 708c2ecf20Sopenharmony_ci goto no_mem; 718c2ecf20Sopenharmony_ci pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); 728c2ecf20Sopenharmony_ci if (!pdev) { 738c2ecf20Sopenharmony_ci kfree(bd); 748c2ecf20Sopenharmony_ci goto no_mem; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci bd->bridge_addr = IP30_RAW_SWIN_BASE(widget); 788c2ecf20Sopenharmony_ci bd->intr_addr = HEART_INTR_ADDR; 798c2ecf20Sopenharmony_ci bd->nasid = 0; 808c2ecf20Sopenharmony_ci bd->masterwid = masterwid; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci bd->mem.name = "Bridge PCI MEM"; 838c2ecf20Sopenharmony_ci bd->mem.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; 848c2ecf20Sopenharmony_ci bd->mem.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; 858c2ecf20Sopenharmony_ci bd->mem.flags = IORESOURCE_MEM; 868c2ecf20Sopenharmony_ci bd->mem_offset = IP30_SWIN_BASE(widget); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci bd->io.name = "Bridge PCI IO"; 898c2ecf20Sopenharmony_ci bd->io.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; 908c2ecf20Sopenharmony_ci bd->io.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; 918c2ecf20Sopenharmony_ci bd->io.flags = IORESOURCE_IO; 928c2ecf20Sopenharmony_ci bd->io_offset = IP30_SWIN_BASE(widget); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci platform_device_add_data(pdev, bd, sizeof(*bd)); 958c2ecf20Sopenharmony_ci platform_device_add(pdev); 968c2ecf20Sopenharmony_ci pr_info("xtalk:%x bridge widget\n", widget); 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cino_mem: 1008c2ecf20Sopenharmony_ci pr_warn("xtalk:%x bridge create out of memory\n", widget); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic unsigned int __init xbow_widget_active(s8 wid) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned int link_stat; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) + 1088c2ecf20Sopenharmony_ci XBOW_REG_LINK_STAT_0 + 1098c2ecf20Sopenharmony_ci XBOW_REG_LINK_BLK_SIZE * 1108c2ecf20Sopenharmony_ci (wid - 8))); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void __init xtalk_init_widget(s8 wid, s8 masterwid) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci xwidget_part_num_t partnum; 1188c2ecf20Sopenharmony_ci widgetreg_t widget_id; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!xbow_widget_active(wid)) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID)); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci partnum = XWIDGET_PART_NUM(widget_id); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci switch (partnum) { 1288c2ecf20Sopenharmony_ci case BRIDGE_WIDGET_PART_NUM: 1298c2ecf20Sopenharmony_ci case XBRIDGE_WIDGET_PART_NUM: 1308c2ecf20Sopenharmony_ci bridge_platform_create(wid, masterwid); 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum); 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int __init ip30_xtalk_init(void) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int i; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Walk widget IDs backwards so that BaseIO is probed first. This 1448c2ecf20Sopenharmony_ci * ensures that the BaseIO IOC3 is always detected as eth0. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--) 1478c2ecf20Sopenharmony_ci xtalk_init_widget(i, IP30_WIDGET_HEART); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciarch_initcall(ip30_xtalk_init); 153