2 * Copyright 2016 Broadcom
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation (the "GPL").
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License version 2 (GPLv2) for more details.
13 * You should have received a copy of the GNU General Public License
14 * version 2 (GPLv2) along with this source code.
17 #include <linux/device.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/pci.h>
22 #include <linux/slab.h>
27 * On 64-bit systems, we do a single ioremap for the whole config space
28 * since we have enough virtual address range available. On 32-bit, we
29 * ioremap the config space for each bus individually.
31 static const bool per_bus_mapping
= !config_enabled(CONFIG_64BIT
);
34 * Create a PCI config space window
35 * - reserve mem region
36 * - alloc struct pci_config_window with space for all mappings
37 * - ioremap the config space
39 struct pci_config_window
*pci_ecam_create(struct device
*dev
,
40 struct resource
*cfgres
, struct resource
*busr
,
41 struct pci_ecam_ops
*ops
)
43 struct pci_config_window
*cfg
;
44 unsigned int bus_range
, bus_range_max
, bsz
;
45 struct resource
*conflict
;
48 if (busr
->start
> busr
->end
)
49 return ERR_PTR(-EINVAL
);
51 cfg
= kzalloc(sizeof(*cfg
), GFP_KERNEL
);
53 return ERR_PTR(-ENOMEM
);
56 cfg
->busr
.start
= busr
->start
;
57 cfg
->busr
.end
= busr
->end
;
58 cfg
->busr
.flags
= IORESOURCE_BUS
;
59 bus_range
= resource_size(&cfg
->busr
);
60 bus_range_max
= resource_size(cfgres
) >> ops
->bus_shift
;
61 if (bus_range
> bus_range_max
) {
62 bus_range
= bus_range_max
;
63 cfg
->busr
.end
= busr
->start
+ bus_range
- 1;
64 dev_warn(dev
, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
65 cfgres
, &cfg
->busr
, busr
);
67 bsz
= 1 << ops
->bus_shift
;
69 cfg
->res
.start
= cfgres
->start
;
70 cfg
->res
.end
= cfgres
->end
;
71 cfg
->res
.flags
= IORESOURCE_MEM
| IORESOURCE_BUSY
;
72 cfg
->res
.name
= "PCI ECAM";
74 conflict
= request_resource_conflict(&iomem_resource
, &cfg
->res
);
77 dev_err(dev
, "can't claim ECAM area %pR: address conflict with %s %pR\n",
78 &cfg
->res
, conflict
->name
, conflict
);
82 if (per_bus_mapping
) {
83 cfg
->winp
= kcalloc(bus_range
, sizeof(*cfg
->winp
), GFP_KERNEL
);
86 for (i
= 0; i
< bus_range
; i
++) {
87 cfg
->winp
[i
] = ioremap(cfgres
->start
+ i
* bsz
, bsz
);
92 cfg
->win
= ioremap(cfgres
->start
, bus_range
* bsz
);
98 err
= ops
->init(dev
, cfg
);
102 dev_info(dev
, "ECAM at %pR for %pR\n", &cfg
->res
, &cfg
->busr
);
106 dev_err(dev
, "ECAM ioremap failed\n");
114 void pci_ecam_free(struct pci_config_window
*cfg
)
118 if (per_bus_mapping
) {
120 for (i
= 0; i
< resource_size(&cfg
->busr
); i
++)
122 iounmap(cfg
->winp
[i
]);
130 release_resource(&cfg
->res
);
135 * Function to implement the pci_ops ->map_bus method
137 void __iomem
*pci_ecam_map_bus(struct pci_bus
*bus
, unsigned int devfn
,
140 struct pci_config_window
*cfg
= bus
->sysdata
;
141 unsigned int devfn_shift
= cfg
->ops
->bus_shift
- 8;
142 unsigned int busn
= bus
->number
;
145 if (busn
< cfg
->busr
.start
|| busn
> cfg
->busr
.end
)
148 busn
-= cfg
->busr
.start
;
150 base
= cfg
->winp
[busn
];
152 base
= cfg
->win
+ (busn
<< cfg
->ops
->bus_shift
);
153 return base
+ (devfn
<< devfn_shift
) + where
;
157 struct pci_ecam_ops pci_generic_ecam_ops
= {
160 .map_bus
= pci_ecam_map_bus
,
161 .read
= pci_generic_config_read
,
162 .write
= pci_generic_config_write
,