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
bridge_platform_create(int widget,int masterwid)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;
43*1e6d11feSLin Yujun struct platform_device *pdev_wd;
44*1e6d11feSLin Yujun struct platform_device *pdev_bd;
457505576dSThomas Bogendoerfer struct resource w1_res;
467505576dSThomas Bogendoerfer
477505576dSThomas Bogendoerfer wd = kzalloc(sizeof(*wd), GFP_KERNEL);
48*1e6d11feSLin Yujun if (!wd) {
49*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge create out of memory\n", widget);
50*1e6d11feSLin Yujun return;
51*1e6d11feSLin Yujun }
527505576dSThomas Bogendoerfer
537505576dSThomas Bogendoerfer snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
547505576dSThomas Bogendoerfer IP30_SWIN_BASE(widget));
557505576dSThomas Bogendoerfer
567505576dSThomas Bogendoerfer memset(&w1_res, 0, sizeof(w1_res));
577505576dSThomas Bogendoerfer w1_res.start = IP30_SWIN_BASE(widget) +
587505576dSThomas Bogendoerfer offsetof(struct bridge_regs, b_nic);
597505576dSThomas Bogendoerfer w1_res.end = w1_res.start + 3;
607505576dSThomas Bogendoerfer w1_res.flags = IORESOURCE_MEM;
617505576dSThomas Bogendoerfer
62*1e6d11feSLin Yujun pdev_wd = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
63*1e6d11feSLin Yujun if (!pdev_wd) {
64*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge create out of memory\n", widget);
65*1e6d11feSLin Yujun goto err_kfree_wd;
667505576dSThomas Bogendoerfer }
67*1e6d11feSLin Yujun if (platform_device_add_resources(pdev_wd, &w1_res, 1)) {
68*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge failed to add platform resources.\n", widget);
69*1e6d11feSLin Yujun goto err_put_pdev_wd;
70*1e6d11feSLin Yujun }
71*1e6d11feSLin Yujun if (platform_device_add_data(pdev_wd, wd, sizeof(*wd))) {
72*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge failed to add platform data.\n", widget);
73*1e6d11feSLin Yujun goto err_put_pdev_wd;
74*1e6d11feSLin Yujun }
75*1e6d11feSLin Yujun if (platform_device_add(pdev_wd)) {
76*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge failed to add platform device.\n", widget);
77*1e6d11feSLin Yujun goto err_put_pdev_wd;
78*1e6d11feSLin Yujun }
79fd27234fSChristophe JAILLET /* platform_device_add_data() duplicates the data */
80fd27234fSChristophe JAILLET kfree(wd);
817505576dSThomas Bogendoerfer
827505576dSThomas Bogendoerfer bd = kzalloc(sizeof(*bd), GFP_KERNEL);
83*1e6d11feSLin Yujun if (!bd) {
84*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge create out of memory\n", widget);
85*1e6d11feSLin Yujun goto err_unregister_pdev_wd;
86*1e6d11feSLin Yujun }
87*1e6d11feSLin Yujun pdev_bd = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
88*1e6d11feSLin Yujun if (!pdev_bd) {
89*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge create out of memory\n", widget);
90*1e6d11feSLin Yujun goto err_kfree_bd;
917505576dSThomas Bogendoerfer }
927505576dSThomas Bogendoerfer
937505576dSThomas Bogendoerfer bd->bridge_addr = IP30_RAW_SWIN_BASE(widget);
947505576dSThomas Bogendoerfer bd->intr_addr = HEART_INTR_ADDR;
957505576dSThomas Bogendoerfer bd->nasid = 0;
967505576dSThomas Bogendoerfer bd->masterwid = masterwid;
977505576dSThomas Bogendoerfer
987505576dSThomas Bogendoerfer bd->mem.name = "Bridge PCI MEM";
997505576dSThomas Bogendoerfer bd->mem.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0;
1007505576dSThomas Bogendoerfer bd->mem.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1;
1017505576dSThomas Bogendoerfer bd->mem.flags = IORESOURCE_MEM;
1027505576dSThomas Bogendoerfer bd->mem_offset = IP30_SWIN_BASE(widget);
1037505576dSThomas Bogendoerfer
1047505576dSThomas Bogendoerfer bd->io.name = "Bridge PCI IO";
1057505576dSThomas Bogendoerfer bd->io.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0;
1067505576dSThomas Bogendoerfer bd->io.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1;
1077505576dSThomas Bogendoerfer bd->io.flags = IORESOURCE_IO;
1087505576dSThomas Bogendoerfer bd->io_offset = IP30_SWIN_BASE(widget);
1097505576dSThomas Bogendoerfer
110*1e6d11feSLin Yujun if (platform_device_add_data(pdev_bd, bd, sizeof(*bd))) {
111*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge failed to add platform data.\n", widget);
112*1e6d11feSLin Yujun goto err_put_pdev_bd;
113*1e6d11feSLin Yujun }
114*1e6d11feSLin Yujun if (platform_device_add(pdev_bd)) {
115*1e6d11feSLin Yujun pr_warn("xtalk:%x bridge failed to add platform device.\n", widget);
116*1e6d11feSLin Yujun goto err_put_pdev_bd;
117*1e6d11feSLin Yujun }
118fd27234fSChristophe JAILLET /* platform_device_add_data() duplicates the data */
119fd27234fSChristophe JAILLET kfree(bd);
1207505576dSThomas Bogendoerfer pr_info("xtalk:%x bridge widget\n", widget);
1217505576dSThomas Bogendoerfer return;
1227505576dSThomas Bogendoerfer
123*1e6d11feSLin Yujun err_put_pdev_bd:
124*1e6d11feSLin Yujun platform_device_put(pdev_bd);
125*1e6d11feSLin Yujun err_kfree_bd:
126*1e6d11feSLin Yujun kfree(bd);
127*1e6d11feSLin Yujun err_unregister_pdev_wd:
128*1e6d11feSLin Yujun platform_device_unregister(pdev_wd);
129*1e6d11feSLin Yujun return;
130*1e6d11feSLin Yujun err_put_pdev_wd:
131*1e6d11feSLin Yujun platform_device_put(pdev_wd);
132*1e6d11feSLin Yujun err_kfree_wd:
133*1e6d11feSLin Yujun kfree(wd);
134*1e6d11feSLin Yujun return;
1357505576dSThomas Bogendoerfer }
1367505576dSThomas Bogendoerfer
xbow_widget_active(s8 wid)1377505576dSThomas Bogendoerfer static unsigned int __init xbow_widget_active(s8 wid)
1387505576dSThomas Bogendoerfer {
1397505576dSThomas Bogendoerfer unsigned int link_stat;
1407505576dSThomas Bogendoerfer
1417505576dSThomas Bogendoerfer link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) +
1427505576dSThomas Bogendoerfer XBOW_REG_LINK_STAT_0 +
1437505576dSThomas Bogendoerfer XBOW_REG_LINK_BLK_SIZE *
1447505576dSThomas Bogendoerfer (wid - 8)));
1457505576dSThomas Bogendoerfer
1467505576dSThomas Bogendoerfer return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0;
1477505576dSThomas Bogendoerfer }
1487505576dSThomas Bogendoerfer
xtalk_init_widget(s8 wid,s8 masterwid)1497505576dSThomas Bogendoerfer static void __init xtalk_init_widget(s8 wid, s8 masterwid)
1507505576dSThomas Bogendoerfer {
1517505576dSThomas Bogendoerfer xwidget_part_num_t partnum;
1527505576dSThomas Bogendoerfer widgetreg_t widget_id;
1537505576dSThomas Bogendoerfer
1547505576dSThomas Bogendoerfer if (!xbow_widget_active(wid))
1557505576dSThomas Bogendoerfer return;
1567505576dSThomas Bogendoerfer
1577505576dSThomas Bogendoerfer widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID));
1587505576dSThomas Bogendoerfer
1597505576dSThomas Bogendoerfer partnum = XWIDGET_PART_NUM(widget_id);
1607505576dSThomas Bogendoerfer
1617505576dSThomas Bogendoerfer switch (partnum) {
1627505576dSThomas Bogendoerfer case BRIDGE_WIDGET_PART_NUM:
1637505576dSThomas Bogendoerfer case XBRIDGE_WIDGET_PART_NUM:
1647505576dSThomas Bogendoerfer bridge_platform_create(wid, masterwid);
1657505576dSThomas Bogendoerfer break;
1667505576dSThomas Bogendoerfer default:
1677505576dSThomas Bogendoerfer pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum);
1687505576dSThomas Bogendoerfer break;
1697505576dSThomas Bogendoerfer }
1707505576dSThomas Bogendoerfer }
1717505576dSThomas Bogendoerfer
ip30_xtalk_init(void)1727505576dSThomas Bogendoerfer static int __init ip30_xtalk_init(void)
1737505576dSThomas Bogendoerfer {
1747505576dSThomas Bogendoerfer int i;
1757505576dSThomas Bogendoerfer
1767505576dSThomas Bogendoerfer /*
1777505576dSThomas Bogendoerfer * Walk widget IDs backwards so that BaseIO is probed first. This
1787505576dSThomas Bogendoerfer * ensures that the BaseIO IOC3 is always detected as eth0.
1797505576dSThomas Bogendoerfer */
1807505576dSThomas Bogendoerfer for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--)
1817505576dSThomas Bogendoerfer xtalk_init_widget(i, IP30_WIDGET_HEART);
1827505576dSThomas Bogendoerfer
1837505576dSThomas Bogendoerfer return 0;
1847505576dSThomas Bogendoerfer }
1857505576dSThomas Bogendoerfer
1867505576dSThomas Bogendoerfer arch_initcall(ip30_xtalk_init);
187