xref: /openbmc/linux/arch/mips/sgi-ip30/ip30-xtalk.c (revision fd27234f)
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