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