123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2018 Marvell International Ltd.
- *
- * https://spdx.org/licenses
- */
- #include <dm.h>
- #include <errno.h>
- #include <fdtdec.h>
- #include <log.h>
- #include <malloc.h>
- #include <pci.h>
- #include <asm/global_data.h>
- #include <asm/io.h>
- #include <linux/ioport.h>
- DECLARE_GLOBAL_DATA_PTR;
- /*
- * This driver supports multiple types of operations / host bridges / busses:
- *
- * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
- * Used to access the internal on-chip devices which are connected
- * to internal buses
- * OTX_PEM: Octeon TX PEM (PCI Express MAC)
- * Used to access the external (off-chip) PCI devices
- * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
- * Used to access the external (off-chip) PCI devices
- */
- enum {
- OTX_ECAM,
- OTX_PEM,
- OTX2_PEM,
- };
- /**
- * struct octeontx_pci - Driver private data
- * @type: Device type matched via compatible (e.g. OTX_ECAM etc)
- * @cfg: Config resource
- * @bus: Bus resource
- */
- struct octeontx_pci {
- unsigned int type;
- struct resource cfg;
- struct resource bus;
- };
- static uintptr_t octeontx_cfg_addr(struct octeontx_pci *pcie,
- int bus_offs, int shift_offs,
- pci_dev_t bdf, uint offset)
- {
- u32 bus, dev, func;
- uintptr_t address;
- bus = PCI_BUS(bdf) + bus_offs;
- dev = PCI_DEV(bdf);
- func = PCI_FUNC(bdf);
- address = (bus << (20 + shift_offs)) |
- (dev << (15 + shift_offs)) |
- (func << (12 + shift_offs)) | offset;
- address += pcie->cfg.start;
- return address;
- }
- static ulong readl_size(uintptr_t addr, enum pci_size_t size)
- {
- ulong val;
- switch (size) {
- case PCI_SIZE_8:
- val = readb(addr);
- break;
- case PCI_SIZE_16:
- val = readw(addr);
- break;
- case PCI_SIZE_32:
- val = readl(addr);
- break;
- default:
- printf("Invalid size\n");
- return -EINVAL;
- };
- return val;
- }
- static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
- {
- switch (size) {
- case PCI_SIZE_8:
- writeb(valuep, addr);
- break;
- case PCI_SIZE_16:
- writew(valuep, addr);
- break;
- case PCI_SIZE_32:
- writel(valuep, addr);
- break;
- default:
- printf("Invalid size\n");
- };
- }
- static bool octeontx_bdf_invalid(pci_dev_t bdf)
- {
- if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
- return true;
- return false;
- }
- static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong *valuep,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
- 0, bdf, offset);
- *valuep = readl_size(address, size);
- debug("%02x.%02x.%02x: u%d %x -> %lx\n",
- PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
- return 0;
- }
- static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong value,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
- 0, bdf, offset);
- writel_size(address, size, value);
- debug("%02x.%02x.%02x: u%d %x <- %lx\n",
- PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
- return 0;
- }
- static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong *valuep,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- u8 hdrtype;
- u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
- u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
- address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4,
- bdf, 0);
- *valuep = pci_conv_32_to_size(~0UL, offset, size);
- if (octeontx_bdf_invalid(bdf))
- return -EPERM;
- *valuep = readl_size(address + offset, size);
- hdrtype = readb(address + PCI_HEADER_TYPE);
- if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
- offset >= PCI_PRIMARY_BUS &&
- offset <= PCI_SUBORDINATE_BUS &&
- *valuep != pci_conv_32_to_size(~0UL, offset, size))
- *valuep -= pci_conv_32_to_size(bus_offs, offset, size);
- return 0;
- }
- static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong value,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- u8 hdrtype;
- u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
- u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
- address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4, bdf, 0);
- hdrtype = readb(address + PCI_HEADER_TYPE);
- if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
- offset >= PCI_PRIMARY_BUS &&
- offset <= PCI_SUBORDINATE_BUS &&
- value != pci_conv_32_to_size(~0UL, offset, size))
- value += pci_conv_32_to_size(bus_offs, offset, size);
- if (octeontx_bdf_invalid(bdf))
- return -EPERM;
- writel_size(address + offset, size, value);
- debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
- PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
- address, value);
- return 0;
- }
- static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong *valuep,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
- bdf, 0);
- *valuep = pci_conv_32_to_size(~0UL, offset, size);
- if (octeontx_bdf_invalid(bdf))
- return -EPERM;
- *valuep = readl_size(address + offset, size);
- debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
- PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
- address, *valuep);
- return 0;
- }
- static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong value,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- struct pci_controller *hose = dev_get_uclass_priv(bus);
- uintptr_t address;
- address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
- bdf, 0);
- if (octeontx_bdf_invalid(bdf))
- return -EPERM;
- writel_size(address + offset, size, value);
- debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
- PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
- address, value);
- return 0;
- }
- int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong *valuep,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- int ret = -EIO;
- switch (pcie->type) {
- case OTX_ECAM:
- ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
- size);
- break;
- case OTX_PEM:
- ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
- size);
- break;
- case OTX2_PEM:
- ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
- size);
- break;
- }
- return ret;
- }
- int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
- uint offset, ulong value,
- enum pci_size_t size)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
- int ret = -EIO;
- switch (pcie->type) {
- case OTX_ECAM:
- ret = octeontx_ecam_write_config(bus, bdf, offset, value,
- size);
- break;
- case OTX_PEM:
- ret = octeontx_pem_write_config(bus, bdf, offset, value,
- size);
- break;
- case OTX2_PEM:
- ret = octeontx2_pem_write_config(bus, bdf, offset, value,
- size);
- break;
- }
- return ret;
- }
- static int pci_octeontx_of_to_plat(struct udevice *dev)
- {
- return 0;
- }
- static int pci_octeontx_probe(struct udevice *dev)
- {
- struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
- int err;
- pcie->type = dev_get_driver_data(dev);
- err = dev_read_resource(dev, 0, &pcie->cfg);
- if (err) {
- debug("Error reading resource: %s\n", fdt_strerror(err));
- return err;
- }
- err = dev_read_pci_bus_range(dev, &pcie->bus);
- if (err) {
- debug("Error reading resource: %s\n", fdt_strerror(err));
- return err;
- }
- return 0;
- }
- static const struct dm_pci_ops pci_octeontx_ops = {
- .read_config = pci_octeontx_read_config,
- .write_config = pci_octeontx_write_config,
- };
- static const struct udevice_id pci_octeontx_ids[] = {
- { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
- { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
- { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
- { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
- { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
- { }
- };
- U_BOOT_DRIVER(pci_octeontx) = {
- .name = "pci_octeontx",
- .id = UCLASS_PCI,
- .of_match = pci_octeontx_ids,
- .ops = &pci_octeontx_ops,
- .of_to_plat = pci_octeontx_of_to_plat,
- .probe = pci_octeontx_probe,
- .priv_auto = sizeof(struct octeontx_pci),
- .flags = DM_FLAG_PRE_RELOC,
- };
|