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 struct pci_ecam_ops
*ops
)
31 struct pci_config_window
*cfg
;
32 unsigned int bus_range
, bus_range_max
, bsz
;
33 struct resource
*conflict
;
36 if (busr
->start
> busr
->end
)
37 return ERR_PTR(-EINVAL
);
39 cfg
= kzalloc(sizeof(*cfg
), GFP_KERNEL
);
41 return ERR_PTR(-ENOMEM
);
45 cfg
->busr
.start
= busr
->start
;
46 cfg
->busr
.end
= busr
->end
;
47 cfg
->busr
.flags
= IORESOURCE_BUS
;
48 bus_range
= resource_size(&cfg
->busr
);
49 bus_range_max
= resource_size(cfgres
) >> ops
->bus_shift
;
50 if (bus_range
> bus_range_max
) {
51 bus_range
= bus_range_max
;
52 cfg
->busr
.end
= busr
->start
+ bus_range
- 1;
53 dev_warn(dev
, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
54 cfgres
, &cfg
->busr
, busr
);
56 bsz
= 1 << ops
->bus_shift
;
58 cfg
->res
.start
= cfgres
->start
;
59 cfg
->res
.end
= cfgres
->end
;
60 cfg
->res
.flags
= IORESOURCE_MEM
| IORESOURCE_BUSY
;
61 cfg
->res
.name
= "PCI ECAM";
63 conflict
= request_resource_conflict(&iomem_resource
, &cfg
->res
);
66 dev_err(dev
, "can't claim ECAM area %pR: address conflict with %s %pR\n",
67 &cfg
->res
, conflict
->name
, conflict
);
71 if (per_bus_mapping
) {
72 cfg
->winp
= kcalloc(bus_range
, sizeof(*cfg
->winp
), GFP_KERNEL
);
75 for (i
= 0; i
< bus_range
; i
++) {
77 pci_remap_cfgspace(cfgres
->start
+ i
* bsz
,
83 cfg
->win
= pci_remap_cfgspace(cfgres
->start
, bus_range
* bsz
);
93 dev_info(dev
, "ECAM at %pR for %pR\n", &cfg
->res
, &cfg
->busr
);
97 dev_err(dev
, "ECAM ioremap failed\n");
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
);
126 * Function to implement the pci_ops ->map_bus method
128 void __iomem
*pci_ecam_map_bus(struct pci_bus
*bus
, unsigned int devfn
,
131 struct pci_config_window
*cfg
= bus
->sysdata
;
132 unsigned int devfn_shift
= cfg
->ops
->bus_shift
- 8;
133 unsigned int busn
= bus
->number
;
136 if (busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
139 busn
-= cfg
->busr
.start
;
141 base
= cfg
->winp
[busn
];
143 base
= cfg
->win
+ (busn
<< cfg
->ops
->bus_shift
);
144 return base
+ (devfn
<< devfn_shift
) + where
;
148 struct pci_ecam_ops pci_generic_ecam_ops
= {
151 .map_bus
= pci_ecam_map_bus
,
152 .read
= pci_generic_config_read
,
153 .write
= pci_generic_config_write
,
157 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
158 /* ECAM ops for 32-bit access only (non-compliant) */
159 struct pci_ecam_ops pci_32b_ops
= {
162 .map_bus
= pci_ecam_map_bus
,
163 .read
= pci_generic_config_read32
,
164 .write
= pci_generic_config_write32
,