MIPS: PCI: use information from 1-wire PROM for IOC3 detection
authorThomas Bogendoerfer <tbogendoerfer@suse.de>
Thu, 3 Oct 2019 09:52:30 +0000 (11:52 +0200)
committerPaul Burton <paul.burton@mips.com>
Mon, 7 Oct 2019 16:47:40 +0000 (09:47 -0700)
IOC3 chips in SGI system are conntected to a bridge ASIC, which has
a 1-wire prom attached with part number information. This changeset
uses this information to create PCI subsystem information, which
the MFD driver uses for further platform device setup.

Signed-off-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Signed-off-by: Paul Burton <paul.burton@mips.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: James Hogan <jhogan@kernel.org>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: linux-serial@vger.kernel.org
arch/mips/include/asm/pci/bridge.h
arch/mips/include/asm/sn/ioc3.h
arch/mips/pci/pci-xtalk-bridge.c
arch/mips/sgi-ip27/ip27-xtalk.c

index a92cd30..3bc630f 100644 (file)
@@ -807,6 +807,7 @@ struct bridge_controller {
        unsigned long           intr_addr;
        struct irq_domain       *domain;
        unsigned int            pci_int[8];
+       u32                     ioc3_sid[8];
        nasid_t                 nasid;
 };
 
index a947eed..78ef760 100644 (file)
@@ -590,4 +590,13 @@ struct ioc3_etxd {
 
 #define MIDR_DATA_MASK         0x0000ffff
 
+/* subsystem IDs supplied by card detection in pci-xtalk-bridge */
+#define        IOC3_SUBSYS_IP27_BASEIO6G       0xc300
+#define        IOC3_SUBSYS_IP27_MIO            0xc301
+#define        IOC3_SUBSYS_IP27_BASEIO         0xc302
+#define        IOC3_SUBSYS_IP29_SYSBOARD       0xc303
+#define        IOC3_SUBSYS_IP30_SYSBOARD       0xc304
+#define        IOC3_SUBSYS_MENET               0xc305
+#define        IOC3_SUBSYS_MENET4              0xc306
+
 #endif /* MIPS_SN_IOC3_H */
index 4bb5e32..3126321 100644 (file)
 #include <linux/dma-direct.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/xtalk-bridge.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/crc16.h>
 
 #include <asm/pci/bridge.h>
 #include <asm/paccess.h>
 #include <asm/sn/irq_alloc.h>
+#include <asm/sn/ioc3.h>
+
+#define CRC16_INIT     0
+#define CRC16_VALID    0xb001
 
 /*
  * Most of the IOC3 PCI config register aren't present
  * we emulate what is needed for a normal PCI enumeration
  */
-static int ioc3_cfg_rd(void *addr, int where, int size, u32 *value)
+static int ioc3_cfg_rd(void *addr, int where, int size, u32 *value, u32 sid)
 {
        u32 cf, shift, mask;
 
@@ -30,6 +36,9 @@ static int ioc3_cfg_rd(void *addr, int where, int size, u32 *value)
                if (get_dbe(cf, (u32 *)addr))
                        return PCIBIOS_DEVICE_NOT_FOUND;
                break;
+       case 0x2c:
+               cf = sid;
+               break;
        case 0x3c:
                /* emulate sane interrupt pin value */
                cf = 0x00000100;
@@ -111,7 +120,8 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn,
         */
        if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) {
                addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2];
-               return ioc3_cfg_rd(addr, where, size, value);
+               return ioc3_cfg_rd(addr, where, size, value,
+                                  bc->ioc3_sid[slot]);
        }
 
        addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)];
@@ -149,7 +159,8 @@ static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn,
         */
        if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) {
                addr = &bridge->b_type1_cfg.c[(fn << 8) | (where & ~3)];
-               return ioc3_cfg_rd(addr, where, size, value);
+               return ioc3_cfg_rd(addr, where, size, value,
+                                  bc->ioc3_sid[slot]);
        }
 
        addr = &bridge->b_type1_cfg.c[(fn << 8) | (where ^ (4 - size))];
@@ -426,6 +437,117 @@ static int bridge_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
        return irq;
 }
 
+#define IOC3_SID(sid)  (PCI_VENDOR_ID_SGI << 16 | (sid))
+
+static void bridge_setup_ip27_baseio6g(struct bridge_controller *bc)
+{
+       bc->ioc3_sid[2] = IOC3_SID(IOC3_SUBSYS_IP27_BASEIO6G);
+       bc->ioc3_sid[6] = IOC3_SID(IOC3_SUBSYS_IP27_MIO);
+}
+
+static void bridge_setup_ip27_baseio(struct bridge_controller *bc)
+{
+       bc->ioc3_sid[2] = IOC3_SID(IOC3_SUBSYS_IP27_BASEIO);
+}
+
+static void bridge_setup_ip29_baseio(struct bridge_controller *bc)
+{
+       bc->ioc3_sid[2] = IOC3_SID(IOC3_SUBSYS_IP29_SYSBOARD);
+}
+
+static void bridge_setup_ip30_sysboard(struct bridge_controller *bc)
+{
+       bc->ioc3_sid[2] = IOC3_SID(IOC3_SUBSYS_IP30_SYSBOARD);
+}
+
+static void bridge_setup_menet(struct bridge_controller *bc)
+{
+       bc->ioc3_sid[0] = IOC3_SID(IOC3_SUBSYS_MENET);
+       bc->ioc3_sid[1] = IOC3_SID(IOC3_SUBSYS_MENET);
+       bc->ioc3_sid[2] = IOC3_SID(IOC3_SUBSYS_MENET);
+       bc->ioc3_sid[3] = IOC3_SID(IOC3_SUBSYS_MENET4);
+}
+
+#define BRIDGE_BOARD_SETUP(_partno, _setup)    \
+       { .match = _partno, .setup = _setup }
+
+static const struct {
+       char *match;
+       void (*setup)(struct bridge_controller *bc);
+} bridge_ioc3_devid[] = {
+       BRIDGE_BOARD_SETUP("030-0734-", bridge_setup_ip27_baseio6g),
+       BRIDGE_BOARD_SETUP("030-0880-", bridge_setup_ip27_baseio6g),
+       BRIDGE_BOARD_SETUP("030-1023-", bridge_setup_ip27_baseio),
+       BRIDGE_BOARD_SETUP("030-1124-", bridge_setup_ip27_baseio),
+       BRIDGE_BOARD_SETUP("030-1025-", bridge_setup_ip29_baseio),
+       BRIDGE_BOARD_SETUP("030-1244-", bridge_setup_ip29_baseio),
+       BRIDGE_BOARD_SETUP("030-1389-", bridge_setup_ip29_baseio),
+       BRIDGE_BOARD_SETUP("030-0887-", bridge_setup_ip30_sysboard),
+       BRIDGE_BOARD_SETUP("030-1467-", bridge_setup_ip30_sysboard),
+       BRIDGE_BOARD_SETUP("030-0873-", bridge_setup_menet),
+};
+
+static void bridge_setup_board(struct bridge_controller *bc, char *partnum)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bridge_ioc3_devid); i++)
+               if (!strncmp(partnum, bridge_ioc3_devid[i].match,
+                            strlen(bridge_ioc3_devid[i].match))) {
+                       bridge_ioc3_devid[i].setup(bc);
+               }
+}
+
+static int bridge_nvmem_match(struct device *dev, const void *data)
+{
+       const char *name = dev_name(dev);
+       const char *prefix = data;
+
+       if (strlen(name) < strlen(prefix))
+               return 0;
+
+       return memcmp(prefix, dev_name(dev), strlen(prefix)) == 0;
+}
+
+static int bridge_get_partnum(u64 baddr, char *partnum)
+{
+       struct nvmem_device *nvmem;
+       char prefix[24];
+       u8 prom[64];
+       int i, j;
+       int ret;
+
+       snprintf(prefix, sizeof(prefix), "bridge-%012llx-0b-", baddr);
+
+       nvmem = nvmem_device_find(prefix, bridge_nvmem_match);
+       if (IS_ERR(nvmem))
+               return PTR_ERR(nvmem);
+
+       ret = nvmem_device_read(nvmem, 0, 64, prom);
+       nvmem_device_put(nvmem);
+
+       if (ret != 64)
+               return ret;
+
+       if (crc16(CRC16_INIT, prom, 32) != CRC16_VALID ||
+           crc16(CRC16_INIT, prom + 32, 32) != CRC16_VALID)
+               return -EINVAL;
+
+       /* Assemble part number */
+       j = 0;
+       for (i = 0; i < 19; i++)
+               if (prom[i + 11] != ' ')
+                       partnum[j++] = prom[i + 11];
+
+       for (i = 0; i < 6; i++)
+               if (prom[i + 32] != ' ')
+                       partnum[j++] = prom[i + 32];
+
+       partnum[j] = 0;
+
+       return 0;
+}
+
 static int bridge_probe(struct platform_device *pdev)
 {
        struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev);
@@ -434,9 +556,14 @@ static int bridge_probe(struct platform_device *pdev)
        struct pci_host_bridge *host;
        struct irq_domain *domain, *parent;
        struct fwnode_handle *fn;
+       char partnum[26];
        int slot;
        int err;
 
+       /* get part number from one wire prom */
+       if (bridge_get_partnum(virt_to_phys((void *)bd->bridge_addr), partnum))
+               return -EPROBE_DEFER; /* not available yet */
+
        parent = irq_get_default_host();
        if (!parent)
                return -ENODEV;
@@ -517,6 +644,8 @@ static int bridge_probe(struct platform_device *pdev)
        }
        bridge_read(bc, b_wid_tflush);    /* wait until Bridge PIO complete */
 
+       bridge_setup_board(bc, partnum);
+
        host->dev.parent = dev;
        host->sysdata = bc;
        host->busnr = 0;
index 5602bb1..5218b90 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/sgi-w1.h>
 #include <linux/platform_data/xtalk-bridge.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/types.h>
 static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
 {
        struct xtalk_bridge_platform_data *bd;
+       struct sgi_w1_platform_data *wd;
        struct platform_device *pdev;
+       struct resource w1_res;
        unsigned long offset;
 
+       offset = NODE_OFFSET(nasid);
+
+       wd = kzalloc(sizeof(*wd), GFP_KERNEL);
+       if (!wd)
+               goto no_mem;
+
+       snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
+                offset + (widget << SWIN_SIZE_BITS));
+
+       memset(&w1_res, 0, sizeof(w1_res));
+       w1_res.start = offset + (widget << SWIN_SIZE_BITS) +
+                               offsetof(struct bridge_regs, b_nic);
+       w1_res.end = w1_res.start + 3;
+       w1_res.flags = IORESOURCE_MEM;
+
+       pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
+       if (!pdev) {
+               kfree(wd);
+               goto no_mem;
+       }
+       platform_device_add_resources(pdev, &w1_res, 1);
+       platform_device_add_data(pdev, wd, sizeof(*wd));
+       platform_device_add(pdev);
+
        bd = kzalloc(sizeof(*bd), GFP_KERNEL);
        if (!bd)
                goto no_mem;
@@ -38,7 +65,6 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
                goto no_mem;
        }
 
-       offset = NODE_OFFSET(nasid);
 
        bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
        bd->intr_addr   = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
@@ -46,14 +72,14 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
        bd->masterwid   = masterwid;
 
        bd->mem.name    = "Bridge PCI MEM";
-       bd->mem.start   = offset + (widget << SWIN_SIZE_BITS);
-       bd->mem.end     = bd->mem.start + SWIN_SIZE - 1;
+       bd->mem.start   = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
+       bd->mem.end     = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
        bd->mem.flags   = IORESOURCE_MEM;
        bd->mem_offset  = offset;
 
        bd->io.name     = "Bridge PCI IO";
-       bd->io.start    = offset + (widget << SWIN_SIZE_BITS);
-       bd->io.end      = bd->io.start + SWIN_SIZE - 1;
+       bd->io.start    = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
+       bd->io.end      = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
        bd->io.flags    = IORESOURCE_IO;
        bd->io_offset   = offset;
 
@@ -81,6 +107,8 @@ static int probe_one_port(nasid_t nasid, int widget, int masterwid)
                bridge_platform_create(nasid, widget, masterwid);
                break;
        default:
+               pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
+                       nasid, widget, partnum);
                break;
        }