* [PATCH 1/3] staging: jnx: PCI quirks for all Juniper platforms
2016-10-07 15:15 [PATCH 0/3] Juniper PCI methods Pantelis Antoniou
@ 2016-10-07 15:15 ` Pantelis Antoniou
2016-10-07 15:15 ` [PATCH 2/3] staging: jnx: Common Juniper PCI methods Pantelis Antoniou
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:15 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Debjit Ghosh, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Rajat Jain,
Shyamshankar Dharmarajan, Pantelis Antoniou, linux-kernel, devel
From: Rajat Jain <rajatjain@juniper.net>
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 <dghosh@juniper.net>
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
Signed-off-by: Shyamshankar Dharmarajan <shyamd@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <groeck@juniper.net>
+ * 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
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/3] staging: jnx: Common Juniper PCI methods
2016-10-07 15:15 [PATCH 0/3] Juniper PCI methods Pantelis Antoniou
2016-10-07 15:15 ` [PATCH 1/3] staging: jnx: PCI quirks for all Juniper platforms Pantelis Antoniou
@ 2016-10-07 15:15 ` Pantelis Antoniou
2016-10-07 16:02 ` Joe Perches
2016-10-07 15:15 ` [PATCH 3/3] staging: jnx: pex8xxx I2C interface driver Pantelis Antoniou
2016-10-07 15:29 ` [PATCH 0/3] Juniper PCI methods Greg Kroah-Hartman
3 siblings, 1 reply; 6+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:15 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Debjit Ghosh, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Rajat Jain,
Shyamshankar Dharmarajan, Pantelis Antoniou, linux-kernel, devel
From: Rajat Jain <rajatjain@juniper.net>
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 <dghosh@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <groeck@juniper.net>
+ * 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, ®32);
+ pci_write_config_dword(dev, PLX_MAGIC_REG, reg32 | PLX_MAGIC_BIT);
+ pci_read_config_dword(dev, PLX_MAGIC_REG, ®32);
+}
+
+/*
+ * Lock back the Read only registers
+ */
+void plx_lock_registers(struct pci_dev *dev)
+{
+ u32 reg32;
+
+ pci_read_config_dword(dev, PLX_MAGIC_REG, ®32);
+ /*
+ * 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, ®32);
+}
+
+/*
+ * 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, ®32);
+ 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, ®32);
+ 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, ®32);
+ 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, ®16);
+ 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, ®32);
+ 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, ®32);
+ 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, ®ion)) {
+ 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, ®ion)) {
+ 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 <rajatjain@juniper.net>
+ * 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
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/3] staging: jnx: pex8xxx I2C interface driver
2016-10-07 15:15 [PATCH 0/3] Juniper PCI methods Pantelis Antoniou
2016-10-07 15:15 ` [PATCH 1/3] staging: jnx: PCI quirks for all Juniper platforms Pantelis Antoniou
2016-10-07 15:15 ` [PATCH 2/3] staging: jnx: Common Juniper PCI methods Pantelis Antoniou
@ 2016-10-07 15:15 ` Pantelis Antoniou
2016-10-07 15:29 ` [PATCH 0/3] Juniper PCI methods Greg Kroah-Hartman
3 siblings, 0 replies; 6+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:15 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Debjit Ghosh, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Rajat Jain,
Shyamshankar Dharmarajan, Pantelis Antoniou, linux-kernel, devel
From: Rajat Jain <rajatjain@juniper.net>
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 <groeck@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <rajatjain@juniper.net>
+ * 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, ®_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,
+ ®val);
+ 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, ®_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, ®val);
+ 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 <rajatjain@juniper.net>");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 0/3] Juniper PCI methods
2016-10-07 15:15 [PATCH 0/3] Juniper PCI methods Pantelis Antoniou
` (2 preceding siblings ...)
2016-10-07 15:15 ` [PATCH 3/3] staging: jnx: pex8xxx I2C interface driver Pantelis Antoniou
@ 2016-10-07 15:29 ` Greg Kroah-Hartman
3 siblings, 0 replies; 6+ messages in thread
From: Greg Kroah-Hartman @ 2016-10-07 15:29 UTC (permalink / raw)
To: Pantelis Antoniou
Cc: Debjit Ghosh, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Rajat Jain,
Shyamshankar Dharmarajan, linux-kernel, devel
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
^ permalink raw reply [flat|nested] 6+ messages in thread