162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for indirect PCI bridges. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998 Gabriel Paubert. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/io.h> 1562306a36Sopenharmony_ci#include <asm/pci-bridge.h> 1662306a36Sopenharmony_ci#include <asm/machdep.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint __indirect_read_config(struct pci_controller *hose, 1962306a36Sopenharmony_ci unsigned char bus_number, unsigned int devfn, 2062306a36Sopenharmony_ci int offset, int len, u32 *val) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci volatile void __iomem *cfg_data; 2362306a36Sopenharmony_ci u8 cfg_type = 0; 2462306a36Sopenharmony_ci u32 bus_no, reg; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) { 2762306a36Sopenharmony_ci if (bus_number != hose->first_busno) 2862306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 2962306a36Sopenharmony_ci if (devfn != 0) 3062306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (ppc_md.pci_exclude_device) 3462306a36Sopenharmony_ci if (ppc_md.pci_exclude_device(hose, bus_number, devfn)) 3562306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) 3862306a36Sopenharmony_ci if (bus_number != hose->first_busno) 3962306a36Sopenharmony_ci cfg_type = 1; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci bus_no = (bus_number == hose->first_busno) ? 4262306a36Sopenharmony_ci hose->self_busno : bus_number; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) 4562306a36Sopenharmony_ci reg = ((offset & 0xf00) << 16) | (offset & 0xfc); 4662306a36Sopenharmony_ci else 4762306a36Sopenharmony_ci reg = offset & 0xfc; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN) 5062306a36Sopenharmony_ci out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 5162306a36Sopenharmony_ci (devfn << 8) | reg | cfg_type)); 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 5462306a36Sopenharmony_ci (devfn << 8) | reg | cfg_type)); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Note: the caller has already checked that offset is 5862306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci cfg_data = hose->cfg_data + (offset & 3); 6162306a36Sopenharmony_ci switch (len) { 6262306a36Sopenharmony_ci case 1: 6362306a36Sopenharmony_ci *val = in_8(cfg_data); 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci case 2: 6662306a36Sopenharmony_ci *val = in_le16(cfg_data); 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci default: 6962306a36Sopenharmony_ci *val = in_le32(cfg_data); 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint indirect_read_config(struct pci_bus *bus, unsigned int devfn, 7662306a36Sopenharmony_ci int offset, int len, u32 *val) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return __indirect_read_config(hose, bus->number, devfn, offset, len, 8162306a36Sopenharmony_ci val); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciint indirect_write_config(struct pci_bus *bus, unsigned int devfn, 8562306a36Sopenharmony_ci int offset, int len, u32 val) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 8862306a36Sopenharmony_ci volatile void __iomem *cfg_data; 8962306a36Sopenharmony_ci u8 cfg_type = 0; 9062306a36Sopenharmony_ci u32 bus_no, reg; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) { 9362306a36Sopenharmony_ci if (bus->number != hose->first_busno) 9462306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 9562306a36Sopenharmony_ci if (devfn != 0) 9662306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (ppc_md.pci_exclude_device) 10062306a36Sopenharmony_ci if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) 10162306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_SET_CFG_TYPE) 10462306a36Sopenharmony_ci if (bus->number != hose->first_busno) 10562306a36Sopenharmony_ci cfg_type = 1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci bus_no = (bus->number == hose->first_busno) ? 10862306a36Sopenharmony_ci hose->self_busno : bus->number; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG) 11162306a36Sopenharmony_ci reg = ((offset & 0xf00) << 16) | (offset & 0xfc); 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci reg = offset & 0xfc; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_BIG_ENDIAN) 11662306a36Sopenharmony_ci out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 11762306a36Sopenharmony_ci (devfn << 8) | reg | cfg_type)); 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 12062306a36Sopenharmony_ci (devfn << 8) | reg | cfg_type)); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* suppress setting of PCI_PRIMARY_BUS */ 12362306a36Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) 12462306a36Sopenharmony_ci if ((offset == PCI_PRIMARY_BUS) && 12562306a36Sopenharmony_ci (bus->number == hose->first_busno)) 12662306a36Sopenharmony_ci val &= 0xffffff00; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Workaround for PCI_28 Errata in 440EPx/GRx */ 12962306a36Sopenharmony_ci if ((hose->indirect_type & PPC_INDIRECT_TYPE_BROKEN_MRM) && 13062306a36Sopenharmony_ci offset == PCI_CACHE_LINE_SIZE) { 13162306a36Sopenharmony_ci val = 0; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * Note: the caller has already checked that offset is 13662306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci cfg_data = hose->cfg_data + (offset & 3); 13962306a36Sopenharmony_ci switch (len) { 14062306a36Sopenharmony_ci case 1: 14162306a36Sopenharmony_ci out_8(cfg_data, val); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case 2: 14462306a36Sopenharmony_ci out_le16(cfg_data, val); 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci default: 14762306a36Sopenharmony_ci out_le32(cfg_data, val); 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct pci_ops indirect_pci_ops = 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci .read = indirect_read_config, 15662306a36Sopenharmony_ci .write = indirect_write_config, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_civoid setup_indirect_pci(struct pci_controller *hose, resource_size_t cfg_addr, 16062306a36Sopenharmony_ci resource_size_t cfg_data, u32 flags) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci resource_size_t base = cfg_addr & PAGE_MASK; 16362306a36Sopenharmony_ci void __iomem *mbase; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mbase = ioremap(base, PAGE_SIZE); 16662306a36Sopenharmony_ci hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK); 16762306a36Sopenharmony_ci if ((cfg_data & PAGE_MASK) != base) 16862306a36Sopenharmony_ci mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); 16962306a36Sopenharmony_ci hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK); 17062306a36Sopenharmony_ci hose->ops = &indirect_pci_ops; 17162306a36Sopenharmony_ci hose->indirect_type = flags; 17262306a36Sopenharmony_ci} 173