indirect_pci.c (4033B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Support for indirect PCI bridges. 4 * 5 * Copyright (C) 1998 Gabriel Paubert. 6 */ 7 8#include <linux/kernel.h> 9#include <linux/pci.h> 10#include <linux/delay.h> 11#include <linux/string.h> 12#include <linux/init.h> 13 14#include <linux/io.h> 15#include <asm/pci-bridge.h> 16 17static int 18indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, 19 int len, u32 *val) 20{ 21 struct pci_controller *hose = pci_bus_to_host(bus); 22 volatile void __iomem *cfg_data; 23 u8 cfg_type = 0; 24 u32 bus_no, reg; 25 26 if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { 27 if (bus->number != hose->first_busno) 28 return PCIBIOS_DEVICE_NOT_FOUND; 29 if (devfn != 0) 30 return PCIBIOS_DEVICE_NOT_FOUND; 31 } 32 33 if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) 34 if (bus->number != hose->first_busno) 35 cfg_type = 1; 36 37 bus_no = (bus->number == hose->first_busno) ? 38 hose->self_busno : bus->number; 39 40 if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) 41 reg = ((offset & 0xf00) << 16) | (offset & 0xfc); 42 else 43 reg = offset & 0xfc; /* Only 3 bits for function */ 44 45 if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) 46 out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 47 (devfn << 8) | reg | cfg_type)); 48 else 49 out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 50 (devfn << 8) | reg | cfg_type)); 51 52 /* 53 * Note: the caller has already checked that offset is 54 * suitably aligned and that len is 1, 2 or 4. 55 */ 56 cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */ 57 switch (len) { 58 case 1: 59 *val = in_8(cfg_data); 60 break; 61 case 2: 62 *val = in_le16(cfg_data); 63 break; 64 default: 65 *val = in_le32(cfg_data); 66 break; 67 } 68 return PCIBIOS_SUCCESSFUL; 69} 70 71static int 72indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, 73 int len, u32 val) 74{ 75 struct pci_controller *hose = pci_bus_to_host(bus); 76 volatile void __iomem *cfg_data; 77 u8 cfg_type = 0; 78 u32 bus_no, reg; 79 80 if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { 81 if (bus->number != hose->first_busno) 82 return PCIBIOS_DEVICE_NOT_FOUND; 83 if (devfn != 0) 84 return PCIBIOS_DEVICE_NOT_FOUND; 85 } 86 87 if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) 88 if (bus->number != hose->first_busno) 89 cfg_type = 1; 90 91 bus_no = (bus->number == hose->first_busno) ? 92 hose->self_busno : bus->number; 93 94 if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) 95 reg = ((offset & 0xf00) << 16) | (offset & 0xfc); 96 else 97 reg = offset & 0xfc; 98 99 if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) 100 out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 101 (devfn << 8) | reg | cfg_type)); 102 else 103 out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | 104 (devfn << 8) | reg | cfg_type)); 105 106 /* suppress setting of PCI_PRIMARY_BUS */ 107 if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) 108 if ((offset == PCI_PRIMARY_BUS) && 109 (bus->number == hose->first_busno)) 110 val &= 0xffffff00; 111 112 /* Workaround for PCI_28 Errata in 440EPx/GRx */ 113 if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) && 114 offset == PCI_CACHE_LINE_SIZE) { 115 val = 0; 116 } 117 118 /* 119 * Note: the caller has already checked that offset is 120 * suitably aligned and that len is 1, 2 or 4. 121 */ 122 cfg_data = hose->cfg_data + (offset & 3); 123 switch (len) { 124 case 1: 125 out_8(cfg_data, val); 126 break; 127 case 2: 128 out_le16(cfg_data, val); 129 break; 130 default: 131 out_le32(cfg_data, val); 132 break; 133 } 134 135 return PCIBIOS_SUCCESSFUL; 136} 137 138static struct pci_ops indirect_pci_ops = { 139 .read = indirect_read_config, 140 .write = indirect_write_config, 141}; 142 143void __init 144setup_indirect_pci(struct pci_controller *hose, 145 resource_size_t cfg_addr, 146 resource_size_t cfg_data, u32 flags) 147{ 148 resource_size_t base = cfg_addr & PAGE_MASK; 149 void __iomem *mbase; 150 151 mbase = ioremap(base, PAGE_SIZE); 152 hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK); 153 if ((cfg_data & PAGE_MASK) != base) 154 mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); 155 hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK); 156 hose->ops = &indirect_pci_ops; 157 hose->indirect_type = flags; 158}