123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support.
- * Copyright (C) 2004-2007 Stanislaw Skowronek <[email protected]>
- * Copyright (C) 2009 Johannes Dickgreber <[email protected]>
- * Copyright (C) 2007, 2014-2016 Joshua Kinard <[email protected]>
- */
- #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);
|