uboot/drivers/pci/pci_octeontx.c
<<
>>
Prefs
   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