root/arch/powerpc/platforms/pasemi/pci.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pa_pxp_offset_valid
  2. pa_pxp_cfg_addr
  3. is_root_port
  4. is_5945_reg
  5. workaround_5945
  6. sb600_set_flag
  7. sb600_set_flag
  8. pa_pxp_read_config
  9. pa_pxp_write_config
  10. setup_pa_pxp
  11. pas_add_bridge
  12. pas_pci_init
  13. pasemi_pci_getcfgaddr

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2006 PA Semi, Inc
   4  *
   5  * Authors: Kip Walker, PA Semi
   6  *          Olof Johansson, PA Semi
   7  *
   8  * Maintained by: Olof Johansson <olof@lixom.net>
   9  *
  10  * Based on arch/powerpc/platforms/maple/pci.c
  11  */
  12 
  13 
  14 #include <linux/kernel.h>
  15 #include <linux/pci.h>
  16 
  17 #include <asm/pci-bridge.h>
  18 #include <asm/isa-bridge.h>
  19 #include <asm/machdep.h>
  20 
  21 #include <asm/ppc-pci.h>
  22 
  23 #include "pasemi.h"
  24 
  25 #define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
  26 
  27 static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
  28 {
  29         /* Device 0 Function 0 is special: It's config space spans function 1 as
  30          * well, so allow larger offset. It's really a two-function device but the
  31          * second function does not probe.
  32          */
  33         if (bus == 0 && devfn == 0)
  34                 return offset < 8192;
  35         else
  36                 return offset < 4096;
  37 }
  38 
  39 static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
  40                                        u8 bus, u8 devfn, int offset)
  41 {
  42         return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset);
  43 }
  44 
  45 static inline int is_root_port(int busno, int devfn)
  46 {
  47         return ((busno == 0) && (PCI_FUNC(devfn) < 4) &&
  48                  ((PCI_SLOT(devfn) == 16) || (PCI_SLOT(devfn) == 17)));
  49 }
  50 
  51 static inline int is_5945_reg(int reg)
  52 {
  53         return (((reg >= 0x18) && (reg < 0x34)) ||
  54                 ((reg >= 0x158) && (reg < 0x178)));
  55 }
  56 
  57 static int workaround_5945(struct pci_bus *bus, unsigned int devfn,
  58                            int offset, int len, u32 *val)
  59 {
  60         struct pci_controller *hose;
  61         void volatile __iomem *addr, *dummy;
  62         int byte;
  63         u32 tmp;
  64 
  65         if (!is_root_port(bus->number, devfn) || !is_5945_reg(offset))
  66                 return 0;
  67 
  68         hose = pci_bus_to_host(bus);
  69 
  70         addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset & ~0x3);
  71         byte = offset & 0x3;
  72 
  73         /* Workaround bug 5945: write 0 to a dummy register before reading,
  74          * and write back what we read. We must read/write the full 32-bit
  75          * contents so we need to shift and mask by hand.
  76          */
  77         dummy = pa_pxp_cfg_addr(hose, bus->number, devfn, 0x10);
  78         out_le32(dummy, 0);
  79         tmp = in_le32(addr);
  80         out_le32(addr, tmp);
  81 
  82         switch (len) {
  83         case 1:
  84                 *val = (tmp >> (8*byte)) & 0xff;
  85                 break;
  86         case 2:
  87                 if (byte == 0)
  88                         *val = tmp & 0xffff;
  89                 else
  90                         *val = (tmp >> 16) & 0xffff;
  91                 break;
  92         default:
  93                 *val = tmp;
  94                 break;
  95         }
  96 
  97         return 1;
  98 }
  99 
 100 #ifdef CONFIG_PPC_PASEMI_NEMO
 101 #define PXP_ERR_CFG_REG 0x4
 102 #define PXP_IGNORE_PCIE_ERRORS  0x800
 103 #define SB600_BUS 5
 104 
 105 static void sb600_set_flag(int bus)
 106 {
 107         static void __iomem *iob_mapbase = NULL;
 108         struct resource res;
 109         struct device_node *dn;
 110         int err;
 111 
 112         if (iob_mapbase == NULL) {
 113                 dn = of_find_compatible_node(NULL, "isa", "pasemi,1682m-iob");
 114                 if (!dn) {
 115                         pr_crit("NEMO SB600 missing iob node\n");
 116                         return;
 117                 }
 118 
 119                 err = of_address_to_resource(dn, 0, &res);
 120                 of_node_put(dn);
 121 
 122                 if (err) {
 123                         pr_crit("NEMO SB600 missing resource\n");
 124                         return;
 125                 }
 126 
 127                 pr_info("NEMO SB600 IOB base %08llx\n",res.start);
 128 
 129                 iob_mapbase = ioremap(res.start + 0x100, 0x94);
 130         }
 131 
 132         if (iob_mapbase != NULL) {
 133                 if (bus == SB600_BUS) {
 134                         /*
 135                          * This is the SB600's bus, tell the PCI-e root port
 136                          * to allow non-zero devices to enumerate.
 137                          */
 138                         out_le32(iob_mapbase + PXP_ERR_CFG_REG, in_le32(iob_mapbase + PXP_ERR_CFG_REG) | PXP_IGNORE_PCIE_ERRORS);
 139                 } else {
 140                         /*
 141                          * Only scan device 0 on other busses
 142                          */
 143                         out_le32(iob_mapbase + PXP_ERR_CFG_REG, in_le32(iob_mapbase + PXP_ERR_CFG_REG) & ~PXP_IGNORE_PCIE_ERRORS);
 144                 }
 145         }
 146 }
 147 
 148 #else
 149 
 150 static void sb600_set_flag(int bus)
 151 {
 152 }
 153 #endif
 154 
 155 static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn,
 156                               int offset, int len, u32 *val)
 157 {
 158         struct pci_controller *hose;
 159         void volatile __iomem *addr;
 160 
 161         hose = pci_bus_to_host(bus);
 162         if (!hose)
 163                 return PCIBIOS_DEVICE_NOT_FOUND;
 164 
 165         if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 166                 return PCIBIOS_BAD_REGISTER_NUMBER;
 167 
 168         if (workaround_5945(bus, devfn, offset, len, val))
 169                 return PCIBIOS_SUCCESSFUL;
 170 
 171         addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
 172 
 173         sb600_set_flag(bus->number);
 174 
 175         /*
 176          * Note: the caller has already checked that offset is
 177          * suitably aligned and that len is 1, 2 or 4.
 178          */
 179         switch (len) {
 180         case 1:
 181                 *val = in_8(addr);
 182                 break;
 183         case 2:
 184                 *val = in_le16(addr);
 185                 break;
 186         default:
 187                 *val = in_le32(addr);
 188                 break;
 189         }
 190 
 191         return PCIBIOS_SUCCESSFUL;
 192 }
 193 
 194 static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn,
 195                                int offset, int len, u32 val)
 196 {
 197         struct pci_controller *hose;
 198         void volatile __iomem *addr;
 199 
 200         hose = pci_bus_to_host(bus);
 201         if (!hose)
 202                 return PCIBIOS_DEVICE_NOT_FOUND;
 203 
 204         if (!pa_pxp_offset_valid(bus->number, devfn, offset))
 205                 return PCIBIOS_BAD_REGISTER_NUMBER;
 206 
 207         addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
 208 
 209         sb600_set_flag(bus->number);
 210 
 211         /*
 212          * Note: the caller has already checked that offset is
 213          * suitably aligned and that len is 1, 2 or 4.
 214          */
 215         switch (len) {
 216         case 1:
 217                 out_8(addr, val);
 218                 break;
 219         case 2:
 220                 out_le16(addr, val);
 221                 break;
 222         default:
 223                 out_le32(addr, val);
 224                 break;
 225         }
 226         return PCIBIOS_SUCCESSFUL;
 227 }
 228 
 229 static struct pci_ops pa_pxp_ops = {
 230         .read = pa_pxp_read_config,
 231         .write = pa_pxp_write_config,
 232 };
 233 
 234 static void __init setup_pa_pxp(struct pci_controller *hose)
 235 {
 236         hose->ops = &pa_pxp_ops;
 237         hose->cfg_data = ioremap(0xe0000000, 0x10000000);
 238 }
 239 
 240 static int __init pas_add_bridge(struct device_node *dev)
 241 {
 242         struct pci_controller *hose;
 243 
 244         pr_debug("Adding PCI host bridge %pOF\n", dev);
 245 
 246         hose = pcibios_alloc_controller(dev);
 247         if (!hose)
 248                 return -ENOMEM;
 249 
 250         hose->first_busno = 0;
 251         hose->last_busno = 0xff;
 252         hose->controller_ops = pasemi_pci_controller_ops;
 253 
 254         setup_pa_pxp(hose);
 255 
 256         pr_info("Found PA-PXP PCI host bridge.\n");
 257 
 258         /* Interpret the "ranges" property */
 259         pci_process_bridge_OF_ranges(hose, dev, 1);
 260 
 261         /*
 262          * Scan for an isa bridge. This is needed to find the SB600 on the nemo
 263          * and does nothing on machines without one.
 264          */
 265         isa_bridge_find_early(hose);
 266 
 267         return 0;
 268 }
 269 
 270 void __init pas_pci_init(void)
 271 {
 272         struct device_node *np, *root;
 273         int res;
 274 
 275         root = of_find_node_by_path("/");
 276         if (!root) {
 277                 pr_crit("pas_pci_init: can't find root of device tree\n");
 278                 return;
 279         }
 280 
 281         pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS);
 282 
 283         np = of_find_compatible_node(root, NULL, "pasemi,rootbus");
 284         if (np) {
 285                 res = pas_add_bridge(np);
 286                 of_node_put(np);
 287         }
 288 }
 289 
 290 void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset)
 291 {
 292         struct pci_controller *hose;
 293 
 294         hose = pci_bus_to_host(dev->bus);
 295 
 296         return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset);
 297 }
 298 
 299 struct pci_controller_ops pasemi_pci_controller_ops;

/* [<][>][^][v][top][bottom][index][help] */