|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support. | 
|  | *   Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org> | 
|  | *   Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de> | 
|  | *   Copyright (C) 2007, 2014-2016 Joshua Kinard <kumba@gentoo.org> | 
|  | */ | 
|  |  | 
|  | #include <linux/init.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/platform_data/sgi-w1.h> | 
|  | #include <linux/platform_data/xtalk-bridge.h> | 
|  |  | 
|  | #include <asm/xtalk/xwidget.h> | 
|  | #include <asm/pci/bridge.h> | 
|  |  | 
|  | #define IP30_SWIN_BASE(widget) \ | 
|  | (0x0000000010000000 | (((unsigned long)(widget)) << 24)) | 
|  |  | 
|  | #define IP30_RAW_SWIN_BASE(widget)	(IO_BASE + IP30_SWIN_BASE(widget)) | 
|  |  | 
|  | #define IP30_SWIN_SIZE		(1 << 24) | 
|  |  | 
|  | #define IP30_WIDGET_XBOW        _AC(0x0, UL)    /* XBow is always 0 */ | 
|  | #define IP30_WIDGET_HEART       _AC(0x8, UL)    /* HEART is always 8 */ | 
|  | #define IP30_WIDGET_PCI_BASE    _AC(0xf, UL)    /* BaseIO PCI is always 15 */ | 
|  |  | 
|  | #define XTALK_NODEV             0xffffffff | 
|  |  | 
|  | #define XBOW_REG_LINK_STAT_0    0x114 | 
|  | #define XBOW_REG_LINK_BLK_SIZE  0x40 | 
|  | #define XBOW_REG_LINK_ALIVE     0x80000000 | 
|  |  | 
|  | #define HEART_INTR_ADDR		0x00000080 | 
|  |  | 
|  | #define xtalk_read	__raw_readl | 
|  |  | 
|  | static void bridge_platform_create(int widget, int masterwid) | 
|  | { | 
|  | struct xtalk_bridge_platform_data *bd; | 
|  | struct sgi_w1_platform_data *wd; | 
|  | struct platform_device *pdev_wd; | 
|  | struct platform_device *pdev_bd; | 
|  | struct resource w1_res; | 
|  |  | 
|  | wd = kzalloc(sizeof(*wd), GFP_KERNEL); | 
|  | if (!wd) { | 
|  | pr_warn("xtalk:%x bridge create out of memory\n", widget); | 
|  | return; | 
|  | } | 
|  |  | 
|  | snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx", | 
|  | IP30_SWIN_BASE(widget)); | 
|  |  | 
|  | memset(&w1_res, 0, sizeof(w1_res)); | 
|  | w1_res.start = IP30_SWIN_BASE(widget) + | 
|  | offsetof(struct bridge_regs, b_nic); | 
|  | w1_res.end = w1_res.start + 3; | 
|  | w1_res.flags = IORESOURCE_MEM; | 
|  |  | 
|  | pdev_wd = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO); | 
|  | if (!pdev_wd) { | 
|  | pr_warn("xtalk:%x bridge create out of memory\n", widget); | 
|  | goto err_kfree_wd; | 
|  | } | 
|  | if (platform_device_add_resources(pdev_wd, &w1_res, 1)) { | 
|  | pr_warn("xtalk:%x bridge failed to add platform resources.\n", widget); | 
|  | goto err_put_pdev_wd; | 
|  | } | 
|  | if (platform_device_add_data(pdev_wd, wd, sizeof(*wd))) { | 
|  | pr_warn("xtalk:%x bridge failed to add platform data.\n", widget); | 
|  | goto err_put_pdev_wd; | 
|  | } | 
|  | if (platform_device_add(pdev_wd)) { | 
|  | pr_warn("xtalk:%x bridge failed to add platform device.\n", widget); | 
|  | goto err_put_pdev_wd; | 
|  | } | 
|  | /* platform_device_add_data() duplicates the data */ | 
|  | kfree(wd); | 
|  |  | 
|  | bd = kzalloc(sizeof(*bd), GFP_KERNEL); | 
|  | if (!bd) { | 
|  | pr_warn("xtalk:%x bridge create out of memory\n", widget); | 
|  | goto err_unregister_pdev_wd; | 
|  | } | 
|  | pdev_bd = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO); | 
|  | if (!pdev_bd) { | 
|  | pr_warn("xtalk:%x bridge create out of memory\n", widget); | 
|  | goto err_kfree_bd; | 
|  | } | 
|  |  | 
|  | bd->bridge_addr	= IP30_RAW_SWIN_BASE(widget); | 
|  | bd->intr_addr	= HEART_INTR_ADDR; | 
|  | bd->nasid	= 0; | 
|  | bd->masterwid	= masterwid; | 
|  |  | 
|  | bd->mem.name	= "Bridge PCI MEM"; | 
|  | bd->mem.start	= IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; | 
|  | bd->mem.end	= IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; | 
|  | bd->mem.flags	= IORESOURCE_MEM; | 
|  | bd->mem_offset	= IP30_SWIN_BASE(widget); | 
|  |  | 
|  | bd->io.name	= "Bridge PCI IO"; | 
|  | bd->io.start	= IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0; | 
|  | bd->io.end	= IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1; | 
|  | bd->io.flags	= IORESOURCE_IO; | 
|  | bd->io_offset	= IP30_SWIN_BASE(widget); | 
|  |  | 
|  | if (platform_device_add_data(pdev_bd, bd, sizeof(*bd))) { | 
|  | pr_warn("xtalk:%x bridge failed to add platform data.\n", widget); | 
|  | goto err_put_pdev_bd; | 
|  | } | 
|  | if (platform_device_add(pdev_bd)) { | 
|  | pr_warn("xtalk:%x bridge failed to add platform device.\n", widget); | 
|  | goto err_put_pdev_bd; | 
|  | } | 
|  | /* platform_device_add_data() duplicates the data */ | 
|  | kfree(bd); | 
|  | pr_info("xtalk:%x bridge widget\n", widget); | 
|  | return; | 
|  |  | 
|  | err_put_pdev_bd: | 
|  | platform_device_put(pdev_bd); | 
|  | err_kfree_bd: | 
|  | kfree(bd); | 
|  | err_unregister_pdev_wd: | 
|  | platform_device_unregister(pdev_wd); | 
|  | return; | 
|  | err_put_pdev_wd: | 
|  | platform_device_put(pdev_wd); | 
|  | err_kfree_wd: | 
|  | kfree(wd); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static unsigned int __init xbow_widget_active(s8 wid) | 
|  | { | 
|  | unsigned int link_stat; | 
|  |  | 
|  | link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) + | 
|  | XBOW_REG_LINK_STAT_0 + | 
|  | XBOW_REG_LINK_BLK_SIZE * | 
|  | (wid - 8))); | 
|  |  | 
|  | return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0; | 
|  | } | 
|  |  | 
|  | static void __init xtalk_init_widget(s8 wid, s8 masterwid) | 
|  | { | 
|  | xwidget_part_num_t partnum; | 
|  | widgetreg_t widget_id; | 
|  |  | 
|  | if (!xbow_widget_active(wid)) | 
|  | return; | 
|  |  | 
|  | widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID)); | 
|  |  | 
|  | partnum = XWIDGET_PART_NUM(widget_id); | 
|  |  | 
|  | switch (partnum) { | 
|  | case BRIDGE_WIDGET_PART_NUM: | 
|  | case XBRIDGE_WIDGET_PART_NUM: | 
|  | bridge_platform_create(wid, masterwid); | 
|  | break; | 
|  | default: | 
|  | pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int __init ip30_xtalk_init(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* | 
|  | * Walk widget IDs backwards so that BaseIO is probed first.  This | 
|  | * ensures that the BaseIO IOC3 is always detected as eth0. | 
|  | */ | 
|  | for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--) | 
|  | xtalk_init_widget(i, IP30_WIDGET_HEART); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | arch_initcall(ip30_xtalk_init); |