Commit 5dc76a96 authored by Thomas Bogendoerfer's avatar Thomas Bogendoerfer Committed by Paul Burton

MIPS: PCI: use information from 1-wire PROM for IOC3 detection

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: default avatarThomas Bogendoerfer <tbogendoerfer@suse.de>
Signed-off-by: default avatarPaul 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
parent 8c2a2b8c
......@@ -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;
};
......
......@@ -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 */
......@@ -11,16 +11,22 @@
#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;
......
......@@ -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>
......@@ -26,9 +27,35 @@
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment