1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright 2016 Broadcom
6 #include <linux/device.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/pci.h>
11 #include <linux/pci-ecam.h>
12 #include <linux/slab.h>
15 * On 64-bit systems, we do a single ioremap for the whole config space
16 * since we have enough virtual address range available. On 32-bit, we
17 * ioremap the config space for each bus individually.
19 static const bool per_bus_mapping
= !IS_ENABLED(CONFIG_64BIT
);
22 * Create a PCI config space window
23 * - reserve mem region
24 * - alloc struct pci_config_window with space for all mappings
25 * - ioremap the config space
27 struct pci_config_window
*pci_ecam_create(struct device
*dev
,
28 struct resource
*cfgres
, struct resource
*busr
,
29 const struct pci_ecam_ops
*ops
)
31 unsigned int bus_shift
= ops
->bus_shift
;
32 struct pci_config_window
*cfg
;
33 unsigned int bus_range
, bus_range_max
, bsz
;
34 struct resource
*conflict
;
37 if (busr
->start
> busr
->end
)
38 return ERR_PTR(-EINVAL
);
40 cfg
= kzalloc(sizeof(*cfg
), GFP_KERNEL
);
42 return ERR_PTR(-ENOMEM
);
44 /* ECAM-compliant platforms need not supply ops->bus_shift */
46 bus_shift
= PCIE_ECAM_BUS_SHIFT
;
50 cfg
->busr
.start
= busr
->start
;
51 cfg
->busr
.end
= busr
->end
;
52 cfg
->busr
.flags
= IORESOURCE_BUS
;
53 cfg
->bus_shift
= bus_shift
;
54 bus_range
= resource_size(&cfg
->busr
);
55 bus_range_max
= resource_size(cfgres
) >> bus_shift
;
56 if (bus_range
> bus_range_max
) {
57 bus_range
= bus_range_max
;
58 resource_set_size(&cfg
->busr
, bus_range
);
59 dev_warn(dev
, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
60 cfgres
, &cfg
->busr
, busr
);
64 cfg
->res
.start
= cfgres
->start
;
65 cfg
->res
.end
= cfgres
->end
;
66 cfg
->res
.flags
= IORESOURCE_MEM
| IORESOURCE_BUSY
;
67 cfg
->res
.name
= "PCI ECAM";
69 conflict
= request_resource_conflict(&iomem_resource
, &cfg
->res
);
72 dev_err(dev
, "can't claim ECAM area %pR: address conflict with %s %pR\n",
73 &cfg
->res
, conflict
->name
, conflict
);
77 if (per_bus_mapping
) {
78 cfg
->winp
= kcalloc(bus_range
, sizeof(*cfg
->winp
), GFP_KERNEL
);
82 cfg
->win
= pci_remap_cfgspace(cfgres
->start
, bus_range
* bsz
);
92 dev_info(dev
, "ECAM at %pR for %pR\n", &cfg
->res
, &cfg
->busr
);
96 dev_err(dev
, "ECAM ioremap failed\n");
103 EXPORT_SYMBOL_GPL(pci_ecam_create
);
105 void pci_ecam_free(struct pci_config_window
*cfg
)
109 if (per_bus_mapping
) {
111 for (i
= 0; i
< resource_size(&cfg
->busr
); i
++)
113 iounmap(cfg
->winp
[i
]);
121 release_resource(&cfg
->res
);
124 EXPORT_SYMBOL_GPL(pci_ecam_free
);
126 static int pci_ecam_add_bus(struct pci_bus
*bus
)
128 struct pci_config_window
*cfg
= bus
->sysdata
;
129 unsigned int bsz
= 1 << cfg
->bus_shift
;
130 unsigned int busn
= bus
->number
;
133 if (!per_bus_mapping
)
136 if (busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
139 busn
-= cfg
->busr
.start
;
140 start
= cfg
->res
.start
+ busn
* bsz
;
142 cfg
->winp
[busn
] = pci_remap_cfgspace(start
, bsz
);
143 if (!cfg
->winp
[busn
])
149 static void pci_ecam_remove_bus(struct pci_bus
*bus
)
151 struct pci_config_window
*cfg
= bus
->sysdata
;
152 unsigned int busn
= bus
->number
;
154 if (!per_bus_mapping
|| busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
157 busn
-= cfg
->busr
.start
;
158 if (cfg
->winp
[busn
]) {
159 iounmap(cfg
->winp
[busn
]);
160 cfg
->winp
[busn
] = NULL
;
165 * Function to implement the pci_ops ->map_bus method
167 void __iomem
*pci_ecam_map_bus(struct pci_bus
*bus
, unsigned int devfn
,
170 struct pci_config_window
*cfg
= bus
->sysdata
;
171 unsigned int bus_shift
= cfg
->ops
->bus_shift
;
172 unsigned int devfn_shift
= cfg
->ops
->bus_shift
- 8;
173 unsigned int busn
= bus
->number
;
175 u32 bus_offset
, devfn_offset
;
177 if (busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
180 busn
-= cfg
->busr
.start
;
181 if (per_bus_mapping
) {
182 base
= cfg
->winp
[busn
];
187 if (cfg
->ops
->bus_shift
) {
188 bus_offset
= (busn
& PCIE_ECAM_BUS_MASK
) << bus_shift
;
189 devfn_offset
= (devfn
& PCIE_ECAM_DEVFN_MASK
) << devfn_shift
;
190 where
&= PCIE_ECAM_REG_MASK
;
192 return base
+ (bus_offset
| devfn_offset
| where
);
195 return base
+ PCIE_ECAM_OFFSET(busn
, devfn
, where
);
197 EXPORT_SYMBOL_GPL(pci_ecam_map_bus
);
200 const struct pci_ecam_ops pci_generic_ecam_ops
= {
202 .add_bus
= pci_ecam_add_bus
,
203 .remove_bus
= pci_ecam_remove_bus
,
204 .map_bus
= pci_ecam_map_bus
,
205 .read
= pci_generic_config_read
,
206 .write
= pci_generic_config_write
,
209 EXPORT_SYMBOL_GPL(pci_generic_ecam_ops
);
211 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
212 /* ECAM ops for 32-bit access only (non-compliant) */
213 const struct pci_ecam_ops pci_32b_ops
= {
215 .add_bus
= pci_ecam_add_bus
,
216 .remove_bus
= pci_ecam_remove_bus
,
217 .map_bus
= pci_ecam_map_bus
,
218 .read
= pci_generic_config_read32
,
219 .write
= pci_generic_config_write32
,
223 /* ECAM ops for 32-bit read only (non-compliant) */
224 const struct pci_ecam_ops pci_32b_read_ops
= {
226 .add_bus
= pci_ecam_add_bus
,
227 .remove_bus
= pci_ecam_remove_bus
,
228 .map_bus
= pci_ecam_map_bus
,
229 .read
= pci_generic_config_read32
,
230 .write
= pci_generic_config_write
,