linux-stable/arch/powerpc/platforms/pasemi/pci.c
Thomas Gleixner 4505153954 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 333
Based on 1 normalized pattern(s):

  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 this program is
  distributed in the hope that it will be useful but without any
  warranty without even the implied warranty of merchantability or
  fitness for a particular purpose see the gnu general public license
  for more details you should have received a copy of the gnu general
  public license along with this program if not write to the free
  software foundation inc 59 temple place suite 330 boston ma 02111
  1307 usa

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-only

has been chosen to replace the boilerplate/reference in 136 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Alexios Zavras <alexios.zavras@intel.com>
Reviewed-by: Allison Randal <allison@lohutok.net>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190530000436.384967451@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-06-05 17:37:06 +02:00

299 lines
6.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2006 PA Semi, Inc
*
* Authors: Kip Walker, PA Semi
* Olof Johansson, PA Semi
*
* Maintained by: Olof Johansson <olof@lixom.net>
*
* Based on arch/powerpc/platforms/maple/pci.c
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <asm/isa-bridge.h>
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include "pasemi.h"
#define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
{
/* Device 0 Function 0 is special: It's config space spans function 1 as
* well, so allow larger offset. It's really a two-function device but the
* second function does not probe.
*/
if (bus == 0 && devfn == 0)
return offset < 8192;
else
return offset < 4096;
}
static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
u8 bus, u8 devfn, int offset)
{
return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset);
}
static inline int is_root_port(int busno, int devfn)
{
return ((busno == 0) && (PCI_FUNC(devfn) < 4) &&
((PCI_SLOT(devfn) == 16) || (PCI_SLOT(devfn) == 17)));
}
static inline int is_5945_reg(int reg)
{
return (((reg >= 0x18) && (reg < 0x34)) ||
((reg >= 0x158) && (reg < 0x178)));
}
static int workaround_5945(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
struct pci_controller *hose;
void volatile __iomem *addr, *dummy;
int byte;
u32 tmp;
if (!is_root_port(bus->number, devfn) || !is_5945_reg(offset))
return 0;
hose = pci_bus_to_host(bus);
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset & ~0x3);
byte = offset & 0x3;
/* Workaround bug 5945: write 0 to a dummy register before reading,
* and write back what we read. We must read/write the full 32-bit
* contents so we need to shift and mask by hand.
*/
dummy = pa_pxp_cfg_addr(hose, bus->number, devfn, 0x10);
out_le32(dummy, 0);
tmp = in_le32(addr);
out_le32(addr, tmp);
switch (len) {
case 1:
*val = (tmp >> (8*byte)) & 0xff;
break;
case 2:
if (byte == 0)
*val = tmp & 0xffff;
else
*val = (tmp >> 16) & 0xffff;
break;
default:
*val = tmp;
break;
}
return 1;
}
#ifdef CONFIG_PPC_PASEMI_NEMO
#define PXP_ERR_CFG_REG 0x4
#define PXP_IGNORE_PCIE_ERRORS 0x800
#define SB600_BUS 5
static void sb600_set_flag(int bus)
{
static void __iomem *iob_mapbase = NULL;
struct resource res;
struct device_node *dn;
int err;
if (iob_mapbase == NULL) {
dn = of_find_compatible_node(NULL, "isa", "pasemi,1682m-iob");
if (!dn) {
pr_crit("NEMO SB600 missing iob node\n");
return;
}
err = of_address_to_resource(dn, 0, &res);
of_node_put(dn);
if (err) {
pr_crit("NEMO SB600 missing resource\n");
return;
}
pr_info("NEMO SB600 IOB base %08llx\n",res.start);
iob_mapbase = ioremap(res.start + 0x100, 0x94);
}
if (iob_mapbase != NULL) {
if (bus == SB600_BUS) {
/*
* This is the SB600's bus, tell the PCI-e root port
* to allow non-zero devices to enumerate.
*/
out_le32(iob_mapbase + PXP_ERR_CFG_REG, in_le32(iob_mapbase + PXP_ERR_CFG_REG) | PXP_IGNORE_PCIE_ERRORS);
} else {
/*
* Only scan device 0 on other busses
*/
out_le32(iob_mapbase + PXP_ERR_CFG_REG, in_le32(iob_mapbase + PXP_ERR_CFG_REG) & ~PXP_IGNORE_PCIE_ERRORS);
}
}
}
#else
static void sb600_set_flag(int bus)
{
}
#endif
static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
struct pci_controller *hose;
void volatile __iomem *addr;
hose = pci_bus_to_host(bus);
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
if (workaround_5945(bus, devfn, offset, len, val))
return PCIBIOS_SUCCESSFUL;
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
sb600_set_flag(bus->number);
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
*/
switch (len) {
case 1:
*val = in_8(addr);
break;
case 2:
*val = in_le16(addr);
break;
default:
*val = in_le32(addr);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val)
{
struct pci_controller *hose;
void volatile __iomem *addr;
hose = pci_bus_to_host(bus);
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
sb600_set_flag(bus->number);
/*
* Note: the caller has already checked that offset is
* suitably aligned and that len is 1, 2 or 4.
*/
switch (len) {
case 1:
out_8(addr, val);
break;
case 2:
out_le16(addr, val);
break;
default:
out_le32(addr, val);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops pa_pxp_ops = {
.read = pa_pxp_read_config,
.write = pa_pxp_write_config,
};
static void __init setup_pa_pxp(struct pci_controller *hose)
{
hose->ops = &pa_pxp_ops;
hose->cfg_data = ioremap(0xe0000000, 0x10000000);
}
static int __init pas_add_bridge(struct device_node *dev)
{
struct pci_controller *hose;
pr_debug("Adding PCI host bridge %pOF\n", dev);
hose = pcibios_alloc_controller(dev);
if (!hose)
return -ENOMEM;
hose->first_busno = 0;
hose->last_busno = 0xff;
hose->controller_ops = pasemi_pci_controller_ops;
setup_pa_pxp(hose);
pr_info("Found PA-PXP PCI host bridge.\n");
/* Interpret the "ranges" property */
pci_process_bridge_OF_ranges(hose, dev, 1);
/*
* Scan for an isa bridge. This is needed to find the SB600 on the nemo
* and does nothing on machines without one.
*/
isa_bridge_find_early(hose);
return 0;
}
void __init pas_pci_init(void)
{
struct device_node *np, *root;
int res;
root = of_find_node_by_path("/");
if (!root) {
pr_crit("pas_pci_init: can't find root of device tree\n");
return;
}
pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS);
np = of_find_compatible_node(root, NULL, "pasemi,rootbus");
if (np) {
res = pas_add_bridge(np);
of_node_put(np);
}
}
void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset)
{
struct pci_controller *hose;
hose = pci_bus_to_host(dev->bus);
return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset);
}
struct pci_controller_ops pasemi_pci_controller_ops;