2016-10-07 15:20:55

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 0/3] Juniper PCI methods

Introduce PCI methods and infrastructure dealing with the
peculiarities of Juniper PTX series of routers.

The Juniper series of routers comprise of both x86 and powerpc
platforms that contain similar hardware components necessitating
common support methods.

Note that this is the first submission and we expect things to be
moved around as required.

This patchset is against mainline as of today: v4.8-9431-g3477d16
and is dependent on the "Juniper prerequisites" and
"Juniper infrastructure" patchsets sent earlier.

Rajat Jain (3):
staging: jnx: PCI quirks for all Juniper platforms
staging: jnx: Common Juniper PCI methods
staging: jnx: pex8xxx I2C interface driver

drivers/staging/jnx/Kconfig | 19 ++
drivers/staging/jnx/Makefile | 3 +
drivers/staging/jnx/jnx-chip-pci-quirks.c | 105 ++++++
drivers/staging/jnx/jnx_common_pci.c | 244 ++++++++++++++
drivers/staging/jnx/pex8xxx_i2c.c | 509 ++++++++++++++++++++++++++++++
include/linux/jnx/jnx_common_pci.h | 49 +++
6 files changed, 929 insertions(+)
create mode 100644 drivers/staging/jnx/jnx-chip-pci-quirks.c
create mode 100644 drivers/staging/jnx/jnx_common_pci.c
create mode 100644 drivers/staging/jnx/pex8xxx_i2c.c
create mode 100644 include/linux/jnx/jnx_common_pci.h

--
1.9.1


2016-10-07 15:19:52

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 2/3] staging: jnx: Common Juniper PCI methods

From: Rajat Jain <[email protected]>

All Juniper PTX platforms, whether powerpc or x86 based, use
similar PCI bridges (PLX & IDT). We don't want to duplicate
code in different arch directories, so put them here.

Signed-off-by: Debjit Ghosh <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
Signed-off-by: Rajat Jain <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/staging/jnx/Kconfig | 6 +
drivers/staging/jnx/Makefile | 1 +
drivers/staging/jnx/jnx_common_pci.c | 244 +++++++++++++++++++++++++++++++++++
include/linux/jnx/jnx_common_pci.h | 49 +++++++
4 files changed, 300 insertions(+)
create mode 100644 drivers/staging/jnx/jnx_common_pci.c
create mode 100644 include/linux/jnx/jnx_common_pci.h

diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index f01ef54..507f10d 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -21,6 +21,12 @@ config JNX_SYSTEM

endmenu

+config JNX_COMMON_PCI
+ bool
+ ---help---
+ JNX common PCI code used by the quircks for configuring the PCIe
+ switches in the system.
+
config JNX_CHIP_PCI_QUIRKS
bool
select PCI_MMCONFIG
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index b29699c..0171476 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -4,3 +4,4 @@

obj-$(CONFIG_JNX_SYSTEM) += jnx-subsys.o jnx-board-core.o
obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
+obj-$(CONFIG_JNX_COMMON_PCI) += jnx_common_pci.o
diff --git a/drivers/staging/jnx/jnx_common_pci.c b/drivers/staging/jnx/jnx_common_pci.c
new file mode 100644
index 0000000..d234465
--- /dev/null
+++ b/drivers/staging/jnx/jnx_common_pci.c
@@ -0,0 +1,244 @@
+/*
+ * Common PTXPMB Board Setup - PCI fixup code
+ *
+ * Guenter Roeck <[email protected]>
+ * Copyright 2012-2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/of_platform.h>
+#include <linux/memblock.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+
+#include <linux/jnx/jnx_common_pci.h>
+
+#define IDT_GASAADDR 0x00000FF8 /* IDT Global Access Addr Reg */
+#define IDT_GASADATA 0x00000FFC /* IDT Global Access Data Reg */
+#define IDT_SWCTL 0x0003E000 /* IDT Switch Ctl Register */
+#define IDT_SWCTL_REGUNLOCK BIT(3) /* "Unlock" bit in Switch Ctl */
+
+#define PLX_MAGIC_REG 0x660
+#define PLX_MAGIC_BIT BIT(30)
+
+/*
+ * Use undocumented PLX technique to unlock all registers
+ * (Including the "Read only" ones)
+ */
+void plx_unlock_registers(struct pci_dev *dev)
+{
+ u32 reg32;
+
+ pci_read_config_dword(dev, PLX_MAGIC_REG, &reg32);
+ pci_write_config_dword(dev, PLX_MAGIC_REG, reg32 | PLX_MAGIC_BIT);
+ pci_read_config_dword(dev, PLX_MAGIC_REG, &reg32);
+}
+
+/*
+ * Lock back the Read only registers
+ */
+void plx_lock_registers(struct pci_dev *dev)
+{
+ u32 reg32;
+
+ pci_read_config_dword(dev, PLX_MAGIC_REG, &reg32);
+ /*
+ * This is a hack. For some reason, if I lock it back, the
+ * INTERRUPT_PIN register is not getting cleared. Will need to talk to
+ * PLX about this. For now, leave it unlocked.
+ * pci_write_config_dword(dev, PLX_MAGIC_REG, reg32 & ~PLX_MAGIC_BIT);
+ */
+ pci_write_config_dword(dev, PLX_MAGIC_REG, reg32 | PLX_MAGIC_BIT);
+ pci_read_config_dword(dev, PLX_MAGIC_REG, &reg32);
+}
+
+/*
+ * Unlock all IDT registers (include read only registers)
+ */
+void idt_48H12G2_unlock_registers(struct pci_dev *dev)
+{
+ u32 reg32;
+
+ pci_write_config_dword(dev, IDT_GASAADDR, IDT_SWCTL);
+ pci_read_config_dword(dev, IDT_GASADATA, &reg32);
+ pci_write_config_dword(dev, IDT_GASAADDR, IDT_SWCTL);
+ pci_write_config_dword(dev, IDT_GASADATA, reg32 | IDT_SWCTL_REGUNLOCK);
+}
+
+/*
+ * Lock back the read only registers.
+ */
+void idt_48H12G2_lock_registers(struct pci_dev *dev)
+{
+ u32 reg32;
+
+ pci_write_config_dword(dev, IDT_GASAADDR, IDT_SWCTL);
+ pci_read_config_dword(dev, IDT_GASADATA, &reg32);
+ pci_write_config_dword(dev, IDT_GASAADDR, IDT_SWCTL);
+ pci_write_config_dword(dev, IDT_GASADATA, reg32 & ~IDT_SWCTL_REGUNLOCK);
+}
+
+/*
+ * Turn a downstream port into hot-pluggable slot (to be managed by hot-plug
+ * driver) by setting the required bits in the capability registers. The
+ * function ensures that only ports with link change notification capability
+ * are turned into hotpluggable slots.
+ */
+static void make_hotpluggable_slot(struct pci_dev *dev)
+{
+ u32 reg32, psn;
+ u16 reg16;
+
+ /*
+ * Only configure downstream ports as hot-pluggable
+ */
+ if (!pci_find_capability(dev, PCI_CAP_ID_EXP) ||
+ pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
+ return;
+
+ /*
+ * There is no point in making it hot-plug if the port
+ * does not support link change notifications.
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &reg32);
+ if (!(reg32 & PCI_EXP_LNKCAP_DLLLARC)) {
+ dev_warn(&dev->dev,
+ "port does not support link state reporting\n");
+ return;
+ }
+
+ /*
+ * Set the PCIe Capability register and the cached copy
+ * Set "Slot Implemented" bit
+ */
+ pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
+ reg16 |= PCI_EXP_FLAGS_SLOT;
+ pcie_capability_write_word(dev, PCI_EXP_FLAGS, reg16);
+ dev->pcie_flags_reg = reg16;
+
+ /*
+ * Use port number as "Slot number"
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &reg32);
+ psn = (reg32 & PCI_EXP_LNKCAP_PN) >> 24;
+ psn = (psn << 19) & PCI_EXP_SLTCAP_PSN;
+
+ /*
+ * Set the Slot capability register
+ * Set "HP capable" and "Physical Slot number"
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &reg32);
+ reg32 &= ~PCI_EXP_SLTCAP_PSN;
+ reg32 |= PCI_EXP_SLTCAP_HPC | psn;
+ pcie_capability_write_dword(dev, PCI_EXP_SLTCAP, reg32);
+}
+
+static int ptx_get_pci_mem_region(struct pci_bus *bus,
+ struct pci_bus_region *region)
+{
+ int i;
+ struct resource *res;
+
+ pci_bus_for_each_resource(bus, res, i) {
+ if (i > PCI_STD_RESOURCE_END && i < PCI_BRIDGE_RESOURCES)
+ continue;
+ if (res && (res->flags & IORESOURCE_MEM)) {
+ pcibios_resource_to_bus(bus, region, res);
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+
+void jnx_fixup_pcie_bridge(struct pci_dev *dev, struct bus_assigns *ap,
+ u16 base)
+{
+ for (; ap->bus; ap++) {
+ if (ap->bus == dev->bus->number &&
+ (ap->slot == ANY || ap->slot == PCI_SLOT(dev->devfn))) {
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS, ap->range);
+ if (ap->flags & PCI_MEM_WINDOW_DISABLED) {
+ pci_write_config_word(dev, PCI_MEMORY_BASE,
+ 0xFFFF);
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT,
+ 0x0000);
+ } else {
+ pci_write_config_word(dev, PCI_MEMORY_BASE,
+ base + ap->offset);
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT,
+ base + ap->offset +
+ ap->limit);
+ }
+ pci_write_config_word(dev, PCI_IO_BASE, 0x0);
+ pci_write_config_word(dev, PCI_COMMAND, ap->command);
+ if (ap->flags & PCI_PORT_IS_HOTPLUGGABLE)
+ make_hotpluggable_slot(dev);
+ if (ap->flags & PCI_PORT_POWERON_DELAY)
+ dev->poweron_delay = 1;
+
+ return;
+ }
+ }
+ dev_err(&dev->dev,
+ "No configuration [bus 0x%x slot 0x%x devfn 0x%x]\n",
+ dev->bus->number, PCI_SLOT(dev->devfn), dev->devfn);
+}
+EXPORT_SYMBOL(jnx_fixup_pcie_bridge);
+
+void ptx_init_pci_bridge(struct pci_dev *dev, struct bus_assigns *ap)
+{
+ struct pci_bus_region region;
+ u32 base;
+
+ /* Get memory base address from parent bus */
+ if (!ptx_get_pci_mem_region(dev->bus->parent, &region)) {
+ base = (region.start >> 16) & 0xfff0;
+ } else {
+ dev_err(&dev->dev, "Can not allocate bridge memory\n");
+ base = 0;
+ }
+ jnx_fixup_pcie_bridge(dev, ap, base);
+}
+EXPORT_SYMBOL(ptx_init_pci_bridge);
+
+static void ptx_init_pci_root_bridge(struct pci_dev *dev)
+{
+ struct pci_bus_region region;
+ u32 base, limit;
+
+ if (!ptx_get_pci_mem_region(dev->bus, &region)) {
+ base = (region.start >> 16) & 0xfff0;
+ limit = (region.end >> 16) & 0xfff0;
+ } else {
+ dev_err(&dev->dev, "Can not allocate root bridge memory\n");
+ base = 0;
+ limit = 0;
+ }
+
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS, 0xff0100);
+ pci_write_config_word(dev, PCI_MEMORY_BASE, base);
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT, limit);
+ pci_write_config_word(dev, PCI_COMMAND, PCI_CMD_MEM_MSTR);
+ dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_P2020E,
+ ptx_init_pci_root_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_P2020,
+ ptx_init_pci_root_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, 0x0450, /* P5040 */
+ ptx_init_pci_root_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_P5020E,
+ ptx_init_pci_root_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_P5020,
+ ptx_init_pci_root_bridge);
+
diff --git a/include/linux/jnx/jnx_common_pci.h b/include/linux/jnx/jnx_common_pci.h
new file mode 100644
index 0000000..c89e2de
--- /dev/null
+++ b/include/linux/jnx/jnx_common_pci.h
@@ -0,0 +1,49 @@
+/*
+ * Common Juniper PCI routine declarations
+ *
+ * Rajat Jain <[email protected]>
+ * Copyright 2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __JNX_COMMON_PCI_H__
+#define __JNX_COMMON_PCI_H__
+
+/*
+ * Normally bus number ranges, memory maps for PCI bridges and hotplug
+ * capabilities should be initialized by the ROMMON or BIOS or u-boot.
+ * Unfortunately, that is not done correctly today. If that happens and
+ * if the PCIe switch is correctly configured in ROMMON/BIOS/uboot,
+ * we may not need this.
+ */
+struct bus_assigns {
+ u8 bus; /* bus number */
+ u8 slot; /* slot number, ANY for all */
+#define ANY 0xff
+ u32 range; /* bus number range. Three bytes <max, min, curr> */
+ u16 offset; /* base address offset relative to parent */
+ u16 limit; /* memory limit (size), added to offset & base */
+ u16 command; /* command to write into command word */
+#define PCI_CMD_MEM_MSTR (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)
+ u32 flags;
+#define PCI_PORT_IS_HOTPLUGGABLE (1 << 0) /* true if port is hotpluggable */
+#define PCI_MEM_WINDOW_DISABLED (1 << 1)
+#define PCI_PORT_POWERON_DELAY (1 << 2) /* true if port needs poweron delay */
+};
+
+/* Common helper routines */
+void jnx_fixup_pcie_bridge(struct pci_dev *, struct bus_assigns *, u16);
+extern void ptx_init_pci_bridge(struct pci_dev *, struct bus_assigns *);
+
+/* PLX routines */
+extern void plx_unlock_registers(struct pci_dev *dev);
+extern void plx_lock_registers(struct pci_dev *dev);
+
+/* IDT routines */
+extern void idt_48H12G2_unlock_registers(struct pci_dev *dev);
+extern void idt_48H12G2_lock_registers(struct pci_dev *dev);
+#endif /* __JNX_COMMON_PCI_H__ */
--
1.9.1

2016-10-07 15:19:43

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 3/3] staging: jnx: pex8xxx I2C interface driver

From: Rajat Jain <[email protected]>

Some Juniper platforms contain an Avago PEX8614, PEX8616 or PEX8713
PCI express switch which is controlled via a I2C interface.

This driver provides a sysfs interface for configuration from
user-space.

Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: Rajat Jain <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/staging/jnx/Kconfig | 7 +
drivers/staging/jnx/Makefile | 1 +
drivers/staging/jnx/pex8xxx_i2c.c | 509 ++++++++++++++++++++++++++++++++++++++
3 files changed, 517 insertions(+)
create mode 100644 drivers/staging/jnx/pex8xxx_i2c.c

diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index 507f10d..b57e93b 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -33,4 +33,11 @@ config JNX_CHIP_PCI_QUIRKS
---help---
PCI quirks for the Juniper chips (ASICs, CPLDs, FPGAs)

+config JNX_PEX8XXX_I2C
+ tristate "PLX PEX8xxx switch I2C driver"
+ depends on I2C
+ ---help---
+ Driver for the I2C interface of PLX PEX8XXX devices
+ (Currently supports PEX8614, PEX8618, PEX8713)
+
endif # JNX_DEVICES
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index 0171476..90526b1 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_JNX_SYSTEM) += jnx-subsys.o jnx-board-core.o
obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
obj-$(CONFIG_JNX_COMMON_PCI) += jnx_common_pci.o
+obj-$(CONFIG_JNX_PEX8XXX_I2C) += pex8xxx_i2c.o
diff --git a/drivers/staging/jnx/pex8xxx_i2c.c b/drivers/staging/jnx/pex8xxx_i2c.c
new file mode 100644
index 0000000..2414121
--- /dev/null
+++ b/drivers/staging/jnx/pex8xxx_i2c.c
@@ -0,0 +1,509 @@
+/*
+ * An I2C driver for the PEX8xxx I2C slave interface
+ *
+ * Rajat Jain <[email protected]>
+ * Copyright 2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/jnx/pci_ids.h>
+
+/* Supported devices */
+enum chips { pex8614, pex8618, pex8713 };
+
+#define MAXSTN 2
+#define MAXMODE 4
+
+/* Common Register defines */
+#define PEX8XXX_CMD(val) (((val) & 7) << 24)
+#define PEX8XXX_CMD_WR 0x03
+#define PEX8XXX_CMD_RD 0x04
+
+#define PEX8XXX_BYTE_ENA(val) (((val) & 0xF) << 10)
+#define BYTE0 0x01
+#define BYTE1 0x02
+#define BYTE2 0x04
+#define BYTE3 0x08
+#define BYTE_ALL 0x0F
+
+#define PEX8XXX_REG(val) (((val) >> 2) & 0x3FF)
+
+/* PEX8614/8618 Device specific register defines */
+#define PEX861X_PORT(val) (((val) & 0x1F) << 15)
+
+#define PEX861X_I2C_CMD(cmd, port, mode, stn, reg, mask) \
+ (PEX8XXX_CMD(cmd) | \
+ PEX861X_PORT(port) | \
+ PEX8XXX_BYTE_ENA(mask) | \
+ PEX8XXX_REG(reg))
+
+/* PEX8713 Device specific register defines */
+#define PEX8713_MODE(val) (((val) & 3) << 20)
+#define PEX8713_MODE_TRANSPARENT 0x00
+#define PEX8713_MODE_NT_LINK 0x01
+#define PEX8713_MODE_NT_VIRT 0x02
+#define PEX8713_MODE_DMA 0x03
+
+#define PEX8713_STN(val) (((val) & 3) << 18)
+
+#define PEX8713_PORT(val) (((val) & 7) << 15)
+
+#define PEX8713_I2C_CMD(cmd, port, mode, stn, reg, mask) \
+ (PEX8XXX_CMD(cmd) | \
+ PEX8713_MODE(mode) | \
+ PEX8713_STN(stn) | \
+ PEX8713_PORT(port) | \
+ PEX8XXX_BYTE_ENA(mask) | \
+ PEX8XXX_REG(reg))
+
+struct pex8xxx_dev {
+ enum chips devtype;
+ u32 reg_addr;
+ u8 port_num;
+ u8 port_mode; /* PEX8713 only */
+ u8 port_stn; /* PEX8713 only */
+ struct attribute_group attr_group;
+ bool (*port_is_valid)(u8 port);
+};
+
+static inline struct pex8xxx_dev *pex8xxx_get_drvdata(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_get_clientdata(client);
+}
+
+static int pex8xxx_read(struct i2c_client *client, u8 stn, u8 mode, u8 mask,
+ u8 port, u32 reg, u32 *val)
+{
+ struct pex8xxx_dev *pex8xxx = i2c_get_clientdata(client);
+ __be32 cmd, data;
+ int ret;
+
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .len = 4,
+ .flags = 0,
+ .buf = (u8 *)&cmd,
+ },
+ {
+ .addr = client->addr,
+ .len = 4,
+ .flags = I2C_M_RD,
+ .buf = (u8 *)&data,
+ },
+ };
+
+ switch (pex8xxx->devtype) {
+ case pex8614:
+ case pex8618:
+ cmd = cpu_to_be32(PEX861X_I2C_CMD(PEX8XXX_CMD_RD, port, mode,
+ stn, reg, mask));
+ break;
+ case pex8713:
+ cmd = cpu_to_be32(PEX8713_I2C_CMD(PEX8XXX_CMD_RD, port, mode,
+ stn, reg, mask));
+ break;
+ default: /* Unknown device */
+ return -ENODEV;
+ }
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ *val = be32_to_cpu(data);
+
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ return 0;
+}
+
+static int pex8xxx_write(struct i2c_client *client, u8 stn, u8 mode, u8 mask,
+ u8 port, u32 reg, u32 val)
+{
+ struct pex8xxx_dev *pex8xxx = i2c_get_clientdata(client);
+ __be32 msgbuf[2];
+ int ret;
+
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .len = 8,
+ .flags = 0,
+ .buf = (u8 *)msgbuf,
+ };
+
+ switch (pex8xxx->devtype) {
+ case pex8614:
+ case pex8618:
+ msgbuf[0] = cpu_to_be32(PEX861X_I2C_CMD(PEX8XXX_CMD_WR, port,
+ mode, stn, reg, mask));
+ break;
+ case pex8713:
+ msgbuf[0] = cpu_to_be32(PEX8713_I2C_CMD(PEX8XXX_CMD_WR, port,
+ mode, stn, reg, mask));
+ break;
+ default: /* Unknown device */
+ return -ENODEV;
+ }
+ msgbuf[1] = cpu_to_be32(val);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ if (ret < 0)
+ return ret;
+ else if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Different PCIe switch can have different port validators.
+ */
+static bool pex8618_port_is_valid(u8 port)
+{
+ return port <= 15;
+}
+
+static bool pex8713_port_is_valid(u8 port)
+{
+ return port <= 5 || (port >= 8 && port <= 13);
+}
+
+static bool pex8614_port_is_valid(u8 port)
+{
+ return port <= 2 ||
+ (port >= 4 && port <= 10) ||
+ port == 12 ||
+ port == 14;
+}
+
+static ssize_t port_num_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", pex8xxx->port_num);
+}
+
+static ssize_t port_num_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+ u8 port_num;
+
+ if (kstrtou8(buf, 0, &port_num))
+ return -EINVAL;
+
+ if (!pex8xxx->port_is_valid(port_num))
+ return -EINVAL;
+
+ pex8xxx->port_num = port_num;
+ return count;
+}
+static DEVICE_ATTR_RW(port_num);
+
+static ssize_t port_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+ char *str;
+
+ switch (pex8xxx->port_mode) {
+ case PEX8713_MODE_TRANSPARENT:
+ str = "transparent";
+ break;
+ case PEX8713_MODE_NT_LINK:
+ str = "nt-link";
+ break;
+ case PEX8713_MODE_NT_VIRT:
+ str = "nt-virtual";
+ break;
+ case PEX8713_MODE_DMA:
+ str = "dma";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+ return sprintf(buf, "%s\n", str);
+}
+
+static ssize_t port_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+
+ if (!strcmp(buf, "transparent\n"))
+ pex8xxx->port_mode = PEX8713_MODE_TRANSPARENT;
+ else if (!strcmp(buf, "nt-link\n"))
+ pex8xxx->port_mode = PEX8713_MODE_NT_LINK;
+ else if (!strcmp(buf, "nt-virtual\n"))
+ pex8xxx->port_mode = PEX8713_MODE_NT_VIRT;
+ else if (!strcmp(buf, "dma\n"))
+ pex8xxx->port_mode = PEX8713_MODE_DMA;
+ else
+ return -EINVAL;
+
+ return count;
+}
+static DEVICE_ATTR_RW(port_mode);
+
+static ssize_t port_stn_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", pex8xxx->port_stn);
+}
+
+static ssize_t port_stn_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+ u8 stn;
+
+ if (kstrtou8(buf, 0, &stn) || (stn >= MAXSTN))
+ return -EINVAL;
+
+ pex8xxx->port_stn = stn;
+
+ return count;
+}
+static DEVICE_ATTR_RW(port_stn);
+
+static ssize_t reg_addr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+
+ return sprintf(buf, "0x%X\n", pex8xxx->reg_addr);
+}
+
+static ssize_t reg_addr_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pex8xxx_dev *pex8xxx = pex8xxx_get_drvdata(dev);
+ unsigned long reg_addr;
+
+ /* PEX8xxx devices support 4K memory per port */
+ if (kstrtoul(buf, 0, &reg_addr) || reg_addr >= 4096 || reg_addr % 4)
+ return -EINVAL;
+
+ pex8xxx->reg_addr = reg_addr;
+
+ return count;
+}
+static DEVICE_ATTR_RW(reg_addr);
+
+static ssize_t reg_value_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pex8xxx_dev *pex8xxx;
+ struct i2c_client *client;
+ u32 regval = 0;
+ int ret;
+
+ client = to_i2c_client(dev);
+ pex8xxx = i2c_get_clientdata(client);
+
+ ret = pex8xxx_read(client, pex8xxx->port_stn, pex8xxx->port_mode,
+ BYTE_ALL, pex8xxx->port_num, pex8xxx->reg_addr,
+ &regval);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "0x%08X\n", regval);
+}
+
+static ssize_t reg_value_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pex8xxx_dev *pex8xxx;
+ struct i2c_client *client;
+ unsigned long reg_val;
+ int retval;
+
+ client = to_i2c_client(dev);
+ pex8xxx = i2c_get_clientdata(client);
+
+ if (kstrtoul(buf, 0, &reg_val))
+ return -EINVAL;
+
+ retval = pex8xxx_write(client, pex8xxx->port_stn, pex8xxx->port_mode,
+ BYTE_ALL, pex8xxx->port_num, pex8xxx->reg_addr,
+ reg_val);
+ if (retval)
+ return retval;
+
+ return count;
+}
+static DEVICE_ATTR_RW(reg_value);
+
+static ssize_t
+port_config_regs_read_bin(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ unsigned int size = 4096;
+ loff_t init_off = off;
+ u32 *buf32 = (u32 *)buf;
+ u32 regval;
+ int ret;
+
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pex8xxx_dev *pex8xxx = i2c_get_clientdata(client);
+
+ if (off > size || off & 3)
+ return 0;
+ if (off + count > size) {
+ size -= off;
+ count = size;
+ } else {
+ size = count;
+ }
+
+ while (size) {
+ ret = pex8xxx_read(client, pex8xxx->port_stn,
+ pex8xxx->port_mode, BYTE_ALL,
+ pex8xxx->port_num, off, &regval);
+ if (ret)
+ regval = 0xDEADBEEF;
+
+ buf32[(off - init_off) / 4] = regval;
+ off += 4;
+ size -= 4;
+ }
+
+ return count;
+}
+static BIN_ATTR(port_config_regs, S_IRUGO, port_config_regs_read_bin,
+ NULL, 4096);
+
+static struct attribute *pex861x_attrs[] = {
+ &dev_attr_port_num.attr,
+ &dev_attr_reg_addr.attr,
+ &dev_attr_reg_value.attr,
+ NULL,
+};
+
+static struct attribute *pex8713_attrs[] = {
+ &dev_attr_port_num.attr,
+ &dev_attr_port_mode.attr,
+ &dev_attr_port_stn.attr,
+ &dev_attr_reg_addr.attr,
+ &dev_attr_reg_value.attr,
+ NULL,
+};
+
+static struct bin_attribute *pex8xxx_bin_attrs[] = {
+ &bin_attr_port_config_regs,
+ NULL,
+};
+
+static int pex8xxx_verify_device(struct pex8xxx_dev *pex8xxx,
+ struct i2c_client *client)
+{
+ u8 stn, mode;
+ bool found = false;
+ u32 data = 0;
+
+ for (stn = 0; stn < MAXSTN; stn++) {
+ for (mode = 0; mode < MAXMODE; mode++) {
+ if (!pex8xxx_read(client, stn, mode, BYTE_ALL, 0,
+ PCI_VENDOR_ID, &data)) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found || (data & 0xFFFF) != PCI_VENDOR_ID_PLX)
+ return -ENODEV;
+
+ switch (data >> 16) {
+ case PCI_DEVICE_ID_PLX_8614:
+ pex8xxx->devtype = pex8614;
+ pex8xxx->port_is_valid = pex8614_port_is_valid;
+ pex8xxx->attr_group.attrs = pex861x_attrs;
+ break;
+ case PCI_DEVICE_ID_PLX_8618:
+ pex8xxx->devtype = pex8618;
+ pex8xxx->port_is_valid = pex8618_port_is_valid;
+ pex8xxx->attr_group.attrs = pex861x_attrs;
+ break;
+ case PCI_DEVICE_ID_PLX_8713:
+ pex8xxx->devtype = pex8713;
+ pex8xxx->port_is_valid = pex8713_port_is_valid;
+ pex8xxx->attr_group.attrs = pex8713_attrs;
+ break;
+ default: /* Unsupported PLX device */
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int pex8xxx_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct pex8xxx_dev *pex8xxx;
+ int retval;
+
+ pex8xxx = devm_kzalloc(&client->dev, sizeof(*pex8xxx), GFP_KERNEL);
+ if (!pex8xxx)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pex8xxx);
+
+ if (pex8xxx_verify_device(pex8xxx, client))
+ return -ENODEV;
+
+ pex8xxx->attr_group.bin_attrs = pex8xxx_bin_attrs;
+
+ retval = sysfs_create_group(&client->dev.kobj, &pex8xxx->attr_group);
+
+ return retval;
+}
+
+static int pex8xxx_remove(struct i2c_client *client)
+{
+ struct pex8xxx_dev *pex8xxx = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &pex8xxx->attr_group);
+ return 0;
+}
+
+static const struct i2c_device_id pex8xxx_id[] = {
+ { "pex8614", pex8614 },
+ { "pex8618", pex8618 },
+ { "pex8713", pex8713 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pex8xxx_id);
+
+static struct i2c_driver pex8xxx_driver = {
+ .driver = {
+ .name = "pex8xxx",
+ },
+ .probe = pex8xxx_probe,
+ .remove = pex8xxx_remove,
+ .id_table = pex8xxx_id,
+};
+
+module_i2c_driver(pex8xxx_driver);
+
+MODULE_DESCRIPTION("PLX PEX8xxx switch I2C interface driver");
+MODULE_AUTHOR("Rajat Jain <[email protected]>");
+MODULE_LICENSE("GPL v2");
--
1.9.1

2016-10-07 15:20:05

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 1/3] staging: jnx: PCI quirks for all Juniper platforms

From: Rajat Jain <[email protected]>

PCI quirks for all juniper platforms. This is located here
since the same PCI devices are present on both PPC and x86
platforms, so per-arch quirks are not appropriate.

Signed-off-by: Debjit Ghosh <[email protected]>
Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
Signed-off-by: Rajat Jain <[email protected]>
Signed-off-by: Shyamshankar Dharmarajan <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/staging/jnx/Kconfig | 6 ++
drivers/staging/jnx/Makefile | 1 +
drivers/staging/jnx/jnx-chip-pci-quirks.c | 105 ++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)
create mode 100644 drivers/staging/jnx/jnx-chip-pci-quirks.c

diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index 5d2b207..f01ef54 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -21,4 +21,10 @@ config JNX_SYSTEM

endmenu

+config JNX_CHIP_PCI_QUIRKS
+ bool
+ select PCI_MMCONFIG
+ ---help---
+ PCI quirks for the Juniper chips (ASICs, CPLDs, FPGAs)
+
endif # JNX_DEVICES
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index 52b8286..b29699c 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -3,3 +3,4 @@
#

obj-$(CONFIG_JNX_SYSTEM) += jnx-subsys.o jnx-board-core.o
+obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
diff --git a/drivers/staging/jnx/jnx-chip-pci-quirks.c b/drivers/staging/jnx/jnx-chip-pci-quirks.c
new file mode 100644
index 0000000..f90b4c7
--- /dev/null
+++ b/drivers/staging/jnx/jnx-chip-pci-quirks.c
@@ -0,0 +1,105 @@
+/*
+ * Common Juniper ASICs - PCI fixup code
+ *
+ * Guenter Roeck <[email protected]>
+ * Copyright 2012-2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation
+ */
+
+#include <linux/bitops.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/jnx/pci_ids.h>
+
+#define INTEL_DEBUG_REG 0x8F8
+#define INTEL_DEBUG_REG_IGNORE_GEN BIT(3)
+
+static void jnx_init_fpga(struct pci_dev *dev)
+{
+ /*
+ * PCI class reported by Juniper FPGAs (SAM/PAM) is
+ * "Unassigned class [ff00]". Change it to something more appropriate.
+ */
+ dev->class = PCI_CLASS_SYSTEM_OTHER << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_SAM,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_SAM_OMEGA,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_SAM_X,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_PAM,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC_P2,
+ jnx_init_fpga);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_OMG_CBC,
+ jnx_init_fpga);
+
+static struct dmi_system_id jnx_asic_pci_bug_affected_platforms[] = {
+ {
+ .ident = "Juniper Networks PTX MLC Card",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Juniper Networks Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "0C0A")
+ },
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(dmi, jnx_asic_pci_bug_affected_platforms);
+
+/*
+ * Juniper Broadway ASICs have an issue where they report incorrect gen type
+ * (Gen-1 / Gen-2) for the PCIe link to the root port.
+ * This workaround needs to be applied to each Intel root port which connects
+ * to a juniper ASIC. It causes the root port to ignore the incorrect fields.
+ */
+static void fixup_intel_root_port(struct pci_dev *dev)
+{
+ struct pci_dev *rootport = dev;
+ u32 tmp32;
+ int ret = 0;
+
+ while (!pci_is_root_bus(rootport->bus))
+ rootport = pci_upstream_bridge(rootport);
+
+ if (rootport->vendor != PCI_VENDOR_ID_INTEL)
+ return;
+
+ ret = pci_read_config_dword(rootport, INTEL_DEBUG_REG, &tmp32);
+ tmp32 |= INTEL_DEBUG_REG_IGNORE_GEN;
+ ret |= pci_write_config_dword(rootport, INTEL_DEBUG_REG, tmp32);
+ if (ret)
+ dev_err(&rootport->dev,
+ "Error applying Intel root port quirk! CONFIG_PCI_MMCONFIG not selected?\n");
+}
+
+/*
+ * The TL/TQ ASICs report their device class as PCI_CLASS_NOT_DEFINED.
+ * Linux does not configure memory access for this class,
+ * so we have to ajust it to something acceptable.
+ */
+static void jnx_init_asic(struct pci_dev *dev)
+{
+ dev->class = PCI_CLASS_NETWORK_OTHER << 8;
+
+ if (dmi_check_system(jnx_asic_pci_bug_affected_platforms))
+ fixup_intel_root_port(dev);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TF,
+ jnx_init_asic);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TL,
+ jnx_init_asic);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_TQ,
+ jnx_init_asic);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_OTN_FRAMER,
+ jnx_init_asic);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_PE,
+ jnx_init_asic);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_PF,
+ jnx_init_asic);
--
1.9.1

2016-10-07 15:29:38

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 0/3] Juniper PCI methods

On Fri, Oct 07, 2016 at 06:15:53PM +0300, Pantelis Antoniou wrote:
> Introduce PCI methods and infrastructure dealing with the
> peculiarities of Juniper PTX series of routers.

<snip>

Same comments as my previous ones, why is this in staging, and make it
all one patch series please.

thanks,

greg k-h