Thomas Gleixner | 4505153 | 2019-05-29 16:57:47 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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 |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 11 | */ |
| 12 | |
| 13 | |
| 14 | #include <linux/kernel.h> |
| 15 | #include <linux/pci.h> |
| 16 | |
| 17 | #include <asm/pci-bridge.h> |
Darren Stevens | 51f4cc2 | 2018-08-19 21:26:28 +0100 | [diff] [blame] | 18 | #include <asm/isa-bridge.h> |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 19 | #include <asm/machdep.h> |
| 20 | |
| 21 | #include <asm/ppc-pci.h> |
| 22 | |
Daniel Axtens | d28a0d9 | 2015-03-31 16:00:52 +1100 | [diff] [blame] | 23 | #include "pasemi.h" |
| 24 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 25 | #define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off)) |
| 26 | |
Olof Johansson | df7e70a | 2007-04-16 16:26:34 +1000 | [diff] [blame] | 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 | } |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 38 | |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 39 | static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose, |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 40 | u8 bus, u8 devfn, int offset) |
| 41 | { |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 42 | return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 43 | } |
| 44 | |
Olof Johansson | 4d44233 | 2007-09-05 12:08:50 +1000 | [diff] [blame] | 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 | |
Darren Stevens | 68f211a | 2018-08-19 21:21:55 +0100 | [diff] [blame] | 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 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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; |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 159 | void volatile __iomem *addr; |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 160 | |
| 161 | hose = pci_bus_to_host(bus); |
| 162 | if (!hose) |
| 163 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 164 | |
Olof Johansson | df7e70a | 2007-04-16 16:26:34 +1000 | [diff] [blame] | 165 | if (!pa_pxp_offset_valid(bus->number, devfn, offset)) |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 166 | return PCIBIOS_BAD_REGISTER_NUMBER; |
| 167 | |
Olof Johansson | 4d44233 | 2007-09-05 12:08:50 +1000 | [diff] [blame] | 168 | if (workaround_5945(bus, devfn, offset, len, val)) |
| 169 | return PCIBIOS_SUCCESSFUL; |
| 170 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 171 | addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); |
| 172 | |
Darren Stevens | 51f4cc2 | 2018-08-19 21:26:28 +0100 | [diff] [blame] | 173 | sb600_set_flag(bus->number); |
| 174 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 181 | *val = in_8(addr); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 182 | break; |
| 183 | case 2: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 184 | *val = in_le16(addr); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 185 | break; |
| 186 | default: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 187 | *val = in_le32(addr); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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; |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 198 | void volatile __iomem *addr; |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 199 | |
| 200 | hose = pci_bus_to_host(bus); |
| 201 | if (!hose) |
| 202 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 203 | |
Olof Johansson | df7e70a | 2007-04-16 16:26:34 +1000 | [diff] [blame] | 204 | if (!pa_pxp_offset_valid(bus->number, devfn, offset)) |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 205 | return PCIBIOS_BAD_REGISTER_NUMBER; |
| 206 | |
| 207 | addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); |
| 208 | |
Darren Stevens | 51f4cc2 | 2018-08-19 21:26:28 +0100 | [diff] [blame] | 209 | sb600_set_flag(bus->number); |
| 210 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 217 | out_8(addr, val); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 218 | break; |
| 219 | case 2: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 220 | out_le16(addr, val); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 221 | break; |
| 222 | default: |
Al Viro | 7c84ace | 2006-10-09 16:23:09 +0100 | [diff] [blame] | 223 | out_le32(addr, val); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 224 | break; |
| 225 | } |
| 226 | return PCIBIOS_SUCCESSFUL; |
| 227 | } |
| 228 | |
| 229 | static struct pci_ops pa_pxp_ops = { |
Nathan Lynch | 1bb8c62 | 2007-08-10 05:18:40 +1000 | [diff] [blame] | 230 | .read = pa_pxp_read_config, |
| 231 | .write = pa_pxp_write_config, |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 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 | |
Arnd Bergmann | 09b55f7 | 2007-06-18 01:06:54 +0200 | [diff] [blame] | 240 | static int __init pas_add_bridge(struct device_node *dev) |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 241 | { |
| 242 | struct pci_controller *hose; |
| 243 | |
Rob Herring | b7c670d | 2017-08-21 10:16:47 -0500 | [diff] [blame] | 244 | pr_debug("Adding PCI host bridge %pOF\n", dev); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 245 | |
| 246 | hose = pcibios_alloc_controller(dev); |
| 247 | if (!hose) |
| 248 | return -ENOMEM; |
| 249 | |
| 250 | hose->first_busno = 0; |
| 251 | hose->last_busno = 0xff; |
Daniel Axtens | d28a0d9 | 2015-03-31 16:00:52 +1100 | [diff] [blame] | 252 | hose->controller_ops = pasemi_pci_controller_ops; |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 253 | |
| 254 | setup_pa_pxp(hose); |
| 255 | |
Darren Stevens | e13606d | 2018-08-03 21:15:10 +1000 | [diff] [blame] | 256 | pr_info("Found PA-PXP PCI host bridge.\n"); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 257 | |
| 258 | /* Interpret the "ranges" property */ |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 259 | pci_process_bridge_OF_ranges(hose, dev, 1); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 260 | |
Darren Stevens | 51f4cc2 | 2018-08-19 21:26:28 +0100 | [diff] [blame] | 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 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 267 | return 0; |
| 268 | } |
| 269 | |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 270 | void __init pas_pci_init(void) |
| 271 | { |
| 272 | struct device_node *np, *root; |
Darren Stevens | 250a935 | 2018-07-25 21:55:18 +0100 | [diff] [blame] | 273 | int res; |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 274 | |
| 275 | root = of_find_node_by_path("/"); |
| 276 | if (!root) { |
Darren Stevens | e13606d | 2018-08-03 21:15:10 +1000 | [diff] [blame] | 277 | pr_crit("pas_pci_init: can't find root of device tree\n"); |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 278 | return; |
| 279 | } |
| 280 | |
Olof Johansson | eff06ef | 2017-12-06 12:03:52 +0100 | [diff] [blame] | 281 | pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS); |
| 282 | |
Darren Stevens | 250a935 | 2018-07-25 21:55:18 +0100 | [diff] [blame] | 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 | } |
Olof Johansson | 1e76875 | 2006-09-06 14:42:08 -0500 | [diff] [blame] | 288 | } |
Olof Johansson | 68c8404 | 2007-09-05 12:08:30 +1000 | [diff] [blame] | 289 | |
| 290 | void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset) |
| 291 | { |
| 292 | struct pci_controller *hose; |
| 293 | |
Olof Johansson | 4d44233 | 2007-09-05 12:08:50 +1000 | [diff] [blame] | 294 | hose = pci_bus_to_host(dev->bus); |
Olof Johansson | 68c8404 | 2007-09-05 12:08:30 +1000 | [diff] [blame] | 295 | |
Olof Johansson | 4d44233 | 2007-09-05 12:08:50 +1000 | [diff] [blame] | 296 | return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset); |
Olof Johansson | 68c8404 | 2007-09-05 12:08:30 +1000 | [diff] [blame] | 297 | } |
Daniel Axtens | d28a0d9 | 2015-03-31 16:00:52 +1100 | [diff] [blame] | 298 | |
| 299 | struct pci_controller_ops pasemi_pci_controller_ops; |