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 bus_range
= resource_size(&cfg
->busr
);
54 bus_range_max
= resource_size(cfgres
) >> bus_shift
;
55 if (bus_range
> bus_range_max
) {
56 bus_range
= bus_range_max
;
57 cfg
->busr
.end
= busr
->start
+ bus_range
- 1;
58 dev_warn(dev
, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
59 cfgres
, &cfg
->busr
, busr
);
63 cfg
->res
.start
= cfgres
->start
;
64 cfg
->res
.end
= cfgres
->end
;
65 cfg
->res
.flags
= IORESOURCE_MEM
| IORESOURCE_BUSY
;
66 cfg
->res
.name
= "PCI ECAM";
68 conflict
= request_resource_conflict(&iomem_resource
, &cfg
->res
);
71 dev_err(dev
, "can't claim ECAM area %pR: address conflict with %s %pR\n",
72 &cfg
->res
, conflict
->name
, conflict
);
76 if (per_bus_mapping
) {
77 cfg
->winp
= kcalloc(bus_range
, sizeof(*cfg
->winp
), GFP_KERNEL
);
80 for (i
= 0; i
< bus_range
; i
++) {
82 pci_remap_cfgspace(cfgres
->start
+ i
* bsz
,
88 cfg
->win
= pci_remap_cfgspace(cfgres
->start
, bus_range
* bsz
);
98 dev_info(dev
, "ECAM at %pR for %pR\n", &cfg
->res
, &cfg
->busr
);
102 dev_err(dev
, "ECAM ioremap failed\n");
109 EXPORT_SYMBOL_GPL(pci_ecam_create
);
111 void pci_ecam_free(struct pci_config_window
*cfg
)
115 if (per_bus_mapping
) {
117 for (i
= 0; i
< resource_size(&cfg
->busr
); i
++)
119 iounmap(cfg
->winp
[i
]);
127 release_resource(&cfg
->res
);
130 EXPORT_SYMBOL_GPL(pci_ecam_free
);
133 * Function to implement the pci_ops ->map_bus method
135 void __iomem
*pci_ecam_map_bus(struct pci_bus
*bus
, unsigned int devfn
,
138 struct pci_config_window
*cfg
= bus
->sysdata
;
139 unsigned int bus_shift
= cfg
->ops
->bus_shift
;
140 unsigned int devfn_shift
= cfg
->ops
->bus_shift
- 8;
141 unsigned int busn
= bus
->number
;
143 u32 bus_offset
, devfn_offset
;
145 if (busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
148 busn
-= cfg
->busr
.start
;
149 if (per_bus_mapping
) {
150 base
= cfg
->winp
[busn
];
155 if (cfg
->ops
->bus_shift
) {
156 bus_offset
= (busn
& PCIE_ECAM_BUS_MASK
) << bus_shift
;
157 devfn_offset
= (devfn
& PCIE_ECAM_DEVFN_MASK
) << devfn_shift
;
158 where
&= PCIE_ECAM_REG_MASK
;
160 return base
+ (bus_offset
| devfn_offset
| where
);
163 return base
+ PCIE_ECAM_OFFSET(busn
, devfn
, where
);
165 EXPORT_SYMBOL_GPL(pci_ecam_map_bus
);
168 const struct pci_ecam_ops pci_generic_ecam_ops
= {
170 .map_bus
= pci_ecam_map_bus
,
171 .read
= pci_generic_config_read
,
172 .write
= pci_generic_config_write
,
175 EXPORT_SYMBOL_GPL(pci_generic_ecam_ops
);
177 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
178 /* ECAM ops for 32-bit access only (non-compliant) */
179 const struct pci_ecam_ops pci_32b_ops
= {
181 .map_bus
= pci_ecam_map_bus
,
182 .read
= pci_generic_config_read32
,
183 .write
= pci_generic_config_write32
,
187 /* ECAM ops for 32-bit read only (non-compliant) */
188 const struct pci_ecam_ops pci_32b_read_ops
= {
190 .map_bus
= pci_ecam_map_bus
,
191 .read
= pci_generic_config_read32
,
192 .write
= pci_generic_config_write
,