17505576dSThomas Bogendoerfer // SPDX-License-Identifier: GPL-2.0 27505576dSThomas Bogendoerfer /* 37505576dSThomas Bogendoerfer * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support. 47505576dSThomas Bogendoerfer * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org> 57505576dSThomas Bogendoerfer * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de> 67505576dSThomas Bogendoerfer * Copyright (C) 2007, 2014-2016 Joshua Kinard <kumba@gentoo.org> 77505576dSThomas Bogendoerfer */ 87505576dSThomas Bogendoerfer 97505576dSThomas Bogendoerfer #include <linux/init.h> 107505576dSThomas Bogendoerfer #include <linux/kernel.h> 117505576dSThomas Bogendoerfer #include <linux/platform_device.h> 127505576dSThomas Bogendoerfer #include <linux/platform_data/sgi-w1.h> 137505576dSThomas Bogendoerfer #include <linux/platform_data/xtalk-bridge.h> 147505576dSThomas Bogendoerfer 157505576dSThomas Bogendoerfer #include <asm/xtalk/xwidget.h> 167505576dSThomas Bogendoerfer #include <asm/pci/bridge.h> 177505576dSThomas Bogendoerfer 187505576dSThomas Bogendoerfer #define IP30_SWIN_BASE(widget) \ 197505576dSThomas Bogendoerfer (0x0000000010000000 | (((unsigned long)(widget)) << 24)) 207505576dSThomas Bogendoerfer 217505576dSThomas Bogendoerfer #define IP30_RAW_SWIN_BASE(widget) (IO_BASE + IP30_SWIN_BASE(widget)) 227505576dSThomas Bogendoerfer 237505576dSThomas Bogendoerfer #define IP30_SWIN_SIZE (1 << 24) 247505576dSThomas Bogendoerfer 257505576dSThomas Bogendoerfer #define IP30_WIDGET_XBOW _AC(0x0, UL) /* XBow is always 0 */ 267505576dSThomas Bogendoerfer #define IP30_WIDGET_HEART _AC(0x8, UL) /* HEART is always 8 */ 277505576dSThomas Bogendoerfer #define IP30_WIDGET_PCI_BASE _AC(0xf, UL) /* BaseIO PCI is always 15 */ 287505576dSThomas Bogendoerfer 297505576dSThomas Bogendoerfer #define XTALK_NODEV 0xffffffff 307505576dSThomas Bogendoerfer 317505576dSThomas Bogendoerfer #define XBOW_REG_LINK_STAT_0 0x114 327505576dSThomas Bogendoerfer #define XBOW_REG_LINK_BLK_SIZE 0x40 337505576dSThomas Bogendoerfer #define XBOW_REG_LINK_ALIVE 0x80000000 347505576dSThomas Bogendoerfer 357505576dSThomas Bogendoerfer #define HEART_INTR_ADDR 0x00000080 367505576dSThomas Bogendoerfer 377505576dSThomas Bogendoerfer #define xtalk_read __raw_readl 387505576dSThomas Bogendoerfer 397505576dSThomas Bogendoerfer static void bridge_platform_create(int widget, int masterwid) 407505576dSThomas Bogendoerfer { 417505576dSThomas Bogendoerfer struct xtalk_bridge_platform_data *bd; 427505576dSThomas Bogendoerfer struct sgi_w1_platform_data *wd; 437505576dSThomas Bogendoerfer struct platform_device *pdev; 447505576dSThomas Bogendoerfer struct resource w1_res; 457505576dSThomas Bogendoerfer 467505576dSThomas Bogendoerfer wd = kzalloc(sizeof(*wd), GFP_KERNEL); 477505576dSThomas Bogendoerfer if (!wd) 487505576dSThomas Bogendoerfer goto no_mem; 497505576dSThomas Bogendoerfer 507505576dSThomas Bogendoerfer snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", 517505576dSThomas Bogendoerfer IP30_SWIN_BASE(widget)); 527505576dSThomas Bogendoerfer 537505576dSThomas Bogendoerfer memset(&w1_res, 0, sizeof(w1_res)); 547505576dSThomas Bogendoerfer w1_res.start = IP30_SWIN_BASE(widget) + 557505576dSThomas Bogendoerfer offsetof(struct bridge_regs, b_nic); 567505576dSThomas Bogendoerfer w1_res.end = w1_res.start + 3; 577505576dSThomas Bogendoerfer w1_res.flags = IORESOURCE_MEM; 587505576dSThomas Bogendoerfer 597505576dSThomas Bogendoerfer pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); 607505576dSThomas Bogendoerfer if (!pdev) { 617505576dSThomas Bogendoerfer kfree(wd); 627505576dSThomas Bogendoerfer goto no_mem; 637505576dSThomas Bogendoerfer } 647505576dSThomas Bogendoerfer platform_device_add_resources(pdev, &w1_res, 1); 657505576dSThomas Bogendoerfer platform_device_add_data(pdev, wd, sizeof(*wd)); 66*fd27234fSChristophe JAILLET /* platform_device_add_data() duplicates the data */ 67*fd27234fSChristophe JAILLET kfree(wd); 687505576dSThomas Bogendoerfer platform_device_add(pdev); 697505576dSThomas Bogendoerfer 707505576dSThomas Bogendoerfer bd = kzalloc(sizeof(*bd), GFP_KERNEL); 717505576dSThomas Bogendoerfer if (!bd) 727505576dSThomas Bogendoerfer goto no_mem; 737505576dSThomas Bogendoerfer pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); 747505576dSThomas Bogendoerfer if (!pdev) { 757505576dSThomas Bogendoerfer kfree(bd); 767505576dSThomas Bogendoerfer goto no_mem; 777505576dSThomas Bogendoerfer } 787505576dSThomas Bogendoerfer 797505576dSThomas Bogendoerfer bd->bridge_addr = IP30_RAW_SWIN_BASE(widget); 807505576dSThomas Bogendoerfer bd->intr_addr = HEART_INTR_ADDR; 817505576dSThomas Bogendoerfer bd->nasid = 0; 827505576dSThomas Bogendoerfer bd->masterwid = masterwid; 837505576dSThomas Bogendoerfer 847505576dSThomas Bogendoerfer bd->mem.name = "Bridge PCI MEM"; 857505576dSThomas Bogendoerfer bd->mem.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; 867505576dSThomas Bogendoerfer bd->mem.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; 877505576dSThomas Bogendoerfer bd->mem.flags = IORESOURCE_MEM; 887505576dSThomas Bogendoerfer bd->mem_offset = IP30_SWIN_BASE(widget); 897505576dSThomas Bogendoerfer 907505576dSThomas Bogendoerfer bd->io.name = "Bridge PCI IO"; 917505576dSThomas Bogendoerfer bd->io.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; 927505576dSThomas Bogendoerfer bd->io.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; 937505576dSThomas Bogendoerfer bd->io.flags = IORESOURCE_IO; 947505576dSThomas Bogendoerfer bd->io_offset = IP30_SWIN_BASE(widget); 957505576dSThomas Bogendoerfer 967505576dSThomas Bogendoerfer platform_device_add_data(pdev, bd, sizeof(*bd)); 97*fd27234fSChristophe JAILLET /* platform_device_add_data() duplicates the data */ 98*fd27234fSChristophe JAILLET kfree(bd); 997505576dSThomas Bogendoerfer platform_device_add(pdev); 1007505576dSThomas Bogendoerfer pr_info("xtalk:%x bridge widget\n", widget); 1017505576dSThomas Bogendoerfer return; 1027505576dSThomas Bogendoerfer 1037505576dSThomas Bogendoerfer no_mem: 1047505576dSThomas Bogendoerfer pr_warn("xtalk:%x bridge create out of memory\n", widget); 1057505576dSThomas Bogendoerfer } 1067505576dSThomas Bogendoerfer 1077505576dSThomas Bogendoerfer static unsigned int __init xbow_widget_active(s8 wid) 1087505576dSThomas Bogendoerfer { 1097505576dSThomas Bogendoerfer unsigned int link_stat; 1107505576dSThomas Bogendoerfer 1117505576dSThomas Bogendoerfer link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) + 1127505576dSThomas Bogendoerfer XBOW_REG_LINK_STAT_0 + 1137505576dSThomas Bogendoerfer XBOW_REG_LINK_BLK_SIZE * 1147505576dSThomas Bogendoerfer (wid - 8))); 1157505576dSThomas Bogendoerfer 1167505576dSThomas Bogendoerfer return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0; 1177505576dSThomas Bogendoerfer } 1187505576dSThomas Bogendoerfer 1197505576dSThomas Bogendoerfer static void __init xtalk_init_widget(s8 wid, s8 masterwid) 1207505576dSThomas Bogendoerfer { 1217505576dSThomas Bogendoerfer xwidget_part_num_t partnum; 1227505576dSThomas Bogendoerfer widgetreg_t widget_id; 1237505576dSThomas Bogendoerfer 1247505576dSThomas Bogendoerfer if (!xbow_widget_active(wid)) 1257505576dSThomas Bogendoerfer return; 1267505576dSThomas Bogendoerfer 1277505576dSThomas Bogendoerfer widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID)); 1287505576dSThomas Bogendoerfer 1297505576dSThomas Bogendoerfer partnum = XWIDGET_PART_NUM(widget_id); 1307505576dSThomas Bogendoerfer 1317505576dSThomas Bogendoerfer switch (partnum) { 1327505576dSThomas Bogendoerfer case BRIDGE_WIDGET_PART_NUM: 1337505576dSThomas Bogendoerfer case XBRIDGE_WIDGET_PART_NUM: 1347505576dSThomas Bogendoerfer bridge_platform_create(wid, masterwid); 1357505576dSThomas Bogendoerfer break; 1367505576dSThomas Bogendoerfer default: 1377505576dSThomas Bogendoerfer pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum); 1387505576dSThomas Bogendoerfer break; 1397505576dSThomas Bogendoerfer } 1407505576dSThomas Bogendoerfer } 1417505576dSThomas Bogendoerfer 1427505576dSThomas Bogendoerfer static int __init ip30_xtalk_init(void) 1437505576dSThomas Bogendoerfer { 1447505576dSThomas Bogendoerfer int i; 1457505576dSThomas Bogendoerfer 1467505576dSThomas Bogendoerfer /* 1477505576dSThomas Bogendoerfer * Walk widget IDs backwards so that BaseIO is probed first. This 1487505576dSThomas Bogendoerfer * ensures that the BaseIO IOC3 is always detected as eth0. 1497505576dSThomas Bogendoerfer */ 1507505576dSThomas Bogendoerfer for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--) 1517505576dSThomas Bogendoerfer xtalk_init_widget(i, IP30_WIDGET_HEART); 1527505576dSThomas Bogendoerfer 1537505576dSThomas Bogendoerfer return 0; 1547505576dSThomas Bogendoerfer } 1557505576dSThomas Bogendoerfer 1567505576dSThomas Bogendoerfer arch_initcall(ip30_xtalk_init); 157