Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756902AbcJGPTw (ORCPT ); Fri, 7 Oct 2016 11:19:52 -0400 Received: from mail-wm0-f47.google.com ([74.125.82.47]:37687 "EHLO mail-wm0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756626AbcJGPSh (ORCPT ); Fri, 7 Oct 2016 11:18:37 -0400 From: Pantelis Antoniou To: Greg Kroah-Hartman Cc: Debjit Ghosh , Georgi Vlaev , Guenter Roeck , JawaharBalaji Thirumalaisamy , Rajat Jain , Shyamshankar Dharmarajan , Pantelis Antoniou , linux-kernel@vger.kernel.org, devel@driverdev.osuosl.org Subject: [PATCH 2/3] staging: jnx: Common Juniper PCI methods Date: Fri, 7 Oct 2016 18:15:55 +0300 Message-Id: <1475853356-21943-3-git-send-email-pantelis.antoniou@konsulko.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1475853356-21943-1-git-send-email-pantelis.antoniou@konsulko.com> References: <1475853356-21943-1-git-send-email-pantelis.antoniou@konsulko.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11484 Lines: 354 From: Rajat Jain 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 Signed-off-by: Guenter Roeck Signed-off-by: JawaharBalaji Thirumalaisamy Signed-off-by: Rajat Jain [Ported from Juniper kernel] Signed-off-by: Pantelis Antoniou --- 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + * 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 */ + 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