1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2018 Marvell International Ltd. 4 * 5 * https://spdx.org/licenses 6 */ 7 8#include <dm.h> 9#include <errno.h> 10#include <fdtdec.h> 11#include <log.h> 12#include <malloc.h> 13#include <pci.h> 14#include <asm/global_data.h> 15 16#include <asm/io.h> 17 18#include <linux/ioport.h> 19 20DECLARE_GLOBAL_DATA_PTR; 21 22/* 23 * This driver supports multiple types of operations / host bridges / busses: 24 * 25 * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism) 26 * Used to access the internal on-chip devices which are connected 27 * to internal buses 28 * OTX_PEM: Octeon TX PEM (PCI Express MAC) 29 * Used to access the external (off-chip) PCI devices 30 * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC) 31 * Used to access the external (off-chip) PCI devices 32 */ 33enum { 34 OTX_ECAM, 35 OTX_PEM, 36 OTX2_PEM, 37}; 38 39/** 40 * struct octeontx_pci - Driver private data 41 * @type: Device type matched via compatible (e.g. OTX_ECAM etc) 42 * @cfg: Config resource 43 * @bus: Bus resource 44 */ 45struct octeontx_pci { 46 unsigned int type; 47 48 struct resource cfg; 49 struct resource bus; 50}; 51 52static ulong readl_size(uintptr_t addr, enum pci_size_t size) 53{ 54 ulong val; 55 56 switch (size) { 57 case PCI_SIZE_8: 58 val = readb(addr); 59 break; 60 case PCI_SIZE_16: 61 val = readw(addr); 62 break; 63 case PCI_SIZE_32: 64 val = readl(addr); 65 break; 66 default: 67 printf("Invalid size\n"); 68 return -EINVAL; 69 }; 70 71 return val; 72} 73 74static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep) 75{ 76 switch (size) { 77 case PCI_SIZE_8: 78 writeb(valuep, addr); 79 break; 80 case PCI_SIZE_16: 81 writew(valuep, addr); 82 break; 83 case PCI_SIZE_32: 84 writel(valuep, addr); 85 break; 86 default: 87 printf("Invalid size\n"); 88 }; 89} 90 91static bool octeontx_bdf_invalid(pci_dev_t bdf) 92{ 93 if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0) 94 return true; 95 96 return false; 97} 98 99static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf, 100 uint offset, ulong *valuep, 101 enum pci_size_t size) 102{ 103 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 104 struct pci_controller *hose = dev_get_uclass_priv(bus); 105 uintptr_t address; 106 107 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno, 108 PCI_DEV(bdf), PCI_FUNC(bdf), offset); 109 *valuep = readl_size(pcie->cfg.start + address, size); 110 111 debug("%02x.%02x.%02x: u%d %x -> %lx\n", 112 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep); 113 114 return 0; 115} 116 117static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf, 118 uint offset, ulong value, 119 enum pci_size_t size) 120{ 121 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 122 struct pci_controller *hose = dev_get_uclass_priv(bus); 123 uintptr_t address; 124 125 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + pcie->bus.start - hose->first_busno, 126 PCI_DEV(bdf), PCI_FUNC(bdf), offset); 127 writel_size(pcie->cfg.start + address, size, value); 128 129 debug("%02x.%02x.%02x: u%d %x <- %lx\n", 130 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value); 131 132 return 0; 133} 134 135static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf, 136 uint offset, ulong *valuep, 137 enum pci_size_t size) 138{ 139 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 140 struct pci_controller *hose = dev_get_uclass_priv(bus); 141 uintptr_t address; 142 u8 hdrtype; 143 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno; 144 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0); 145 146 *valuep = pci_conv_32_to_size(~0UL, offset, size); 147 148 if (octeontx_bdf_invalid(bdf)) 149 return -EPERM; 150 151 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno, 152 PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4; 153 *valuep = readl_size(pcie->cfg.start + address + offset, size); 154 155 hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE); 156 if (hdrtype == PCI_HEADER_TYPE_BRIDGE && 157 offset >= PCI_PRIMARY_BUS && 158 offset <= PCI_SUBORDINATE_BUS && 159 *valuep != pci_conv_32_to_size(~0UL, offset, size)) 160 *valuep -= pci_conv_32_to_size(bus_offs, offset, size); 161 162 return 0; 163} 164 165static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf, 166 uint offset, ulong value, 167 enum pci_size_t size) 168{ 169 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 170 struct pci_controller *hose = dev_get_uclass_priv(bus); 171 uintptr_t address; 172 u8 hdrtype; 173 u8 pri_bus = pcie->bus.start + 1 - hose->first_busno; 174 u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0); 175 176 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno, 177 PCI_DEV(bdf), PCI_FUNC(bdf), 0) << 4; 178 179 hdrtype = readb(pcie->cfg.start + address + PCI_HEADER_TYPE); 180 if (hdrtype == PCI_HEADER_TYPE_BRIDGE && 181 offset >= PCI_PRIMARY_BUS && 182 offset <= PCI_SUBORDINATE_BUS && 183 value != pci_conv_32_to_size(~0UL, offset, size)) 184 value += pci_conv_32_to_size(bus_offs, offset, size); 185 186 if (octeontx_bdf_invalid(bdf)) 187 return -EPERM; 188 189 writel_size(pcie->cfg.start + address + offset, size, value); 190 191 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n", 192 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, 193 address, value); 194 195 return 0; 196} 197 198static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf, 199 uint offset, ulong *valuep, 200 enum pci_size_t size) 201{ 202 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 203 struct pci_controller *hose = dev_get_uclass_priv(bus); 204 uintptr_t address; 205 206 *valuep = pci_conv_32_to_size(~0UL, offset, size); 207 208 if (octeontx_bdf_invalid(bdf)) 209 return -EPERM; 210 211 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno, 212 PCI_DEV(bdf), PCI_FUNC(bdf), offset); 213 *valuep = readl_size(pcie->cfg.start + address, size); 214 215 debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n", 216 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, 217 address, *valuep); 218 219 return 0; 220} 221 222static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf, 223 uint offset, ulong value, 224 enum pci_size_t size) 225{ 226 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 227 struct pci_controller *hose = dev_get_uclass_priv(bus); 228 uintptr_t address; 229 230 if (octeontx_bdf_invalid(bdf)) 231 return -EPERM; 232 233 address = PCIE_ECAM_OFFSET(PCI_BUS(bdf) + 1 - hose->first_busno, 234 PCI_DEV(bdf), PCI_FUNC(bdf), offset); 235 writel_size(pcie->cfg.start + address, size, value); 236 237 debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n", 238 PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, 239 address, value); 240 241 return 0; 242} 243 244int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf, 245 uint offset, ulong *valuep, 246 enum pci_size_t size) 247{ 248 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 249 int ret = -EIO; 250 251 switch (pcie->type) { 252 case OTX_ECAM: 253 ret = octeontx_ecam_read_config(bus, bdf, offset, valuep, 254 size); 255 break; 256 case OTX_PEM: 257 ret = octeontx_pem_read_config(bus, bdf, offset, valuep, 258 size); 259 break; 260 case OTX2_PEM: 261 ret = octeontx2_pem_read_config(bus, bdf, offset, valuep, 262 size); 263 break; 264 } 265 266 return ret; 267} 268 269int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf, 270 uint offset, ulong value, 271 enum pci_size_t size) 272{ 273 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus); 274 int ret = -EIO; 275 276 switch (pcie->type) { 277 case OTX_ECAM: 278 ret = octeontx_ecam_write_config(bus, bdf, offset, value, 279 size); 280 break; 281 case OTX_PEM: 282 ret = octeontx_pem_write_config(bus, bdf, offset, value, 283 size); 284 break; 285 case OTX2_PEM: 286 ret = octeontx2_pem_write_config(bus, bdf, offset, value, 287 size); 288 break; 289 } 290 291 return ret; 292} 293 294static int pci_octeontx_of_to_plat(struct udevice *dev) 295{ 296 return 0; 297} 298 299static int pci_octeontx_probe(struct udevice *dev) 300{ 301 struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev); 302 int err; 303 304 pcie->type = dev_get_driver_data(dev); 305 306 err = dev_read_resource(dev, 0, &pcie->cfg); 307 if (err) { 308 debug("Error reading resource: %s\n", fdt_strerror(err)); 309 return err; 310 } 311 312 err = dev_read_pci_bus_range(dev, &pcie->bus); 313 if (err) { 314 debug("Error reading resource: %s\n", fdt_strerror(err)); 315 return err; 316 } 317 318 return 0; 319} 320 321static const struct dm_pci_ops pci_octeontx_ops = { 322 .read_config = pci_octeontx_read_config, 323 .write_config = pci_octeontx_write_config, 324}; 325 326static const struct udevice_id pci_octeontx_ids[] = { 327 { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM }, 328 { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM }, 329 { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM }, 330 { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM }, 331 { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM }, 332 { } 333}; 334 335U_BOOT_DRIVER(pci_octeontx) = { 336 .name = "pci_octeontx", 337 .id = UCLASS_PCI, 338 .of_match = pci_octeontx_ids, 339 .ops = &pci_octeontx_ops, 340 .of_to_plat = pci_octeontx_of_to_plat, 341 .probe = pci_octeontx_probe, 342 .priv_auto = sizeof(struct octeontx_pci), 343 .flags = DM_FLAG_PRE_RELOC, 344}; 345