2016-03-12 00:35:55 +03:00
/*
2016-08-23 00:59:48 +03:00
* Generic PCI host driver common code
*
2016-03-12 00:35:55 +03:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
* Copyright ( C ) 2014 ARM Limited
*
* Author : Will Deacon < will . deacon @ arm . com >
*/
# include <linux/kernel.h>
# include <linux/of_address.h>
# include <linux/of_pci.h>
2016-06-10 22:55:09 +03:00
# include <linux/pci-ecam.h>
2016-03-12 00:35:55 +03:00
# include <linux/platform_device.h>
2016-05-12 01:34:46 +03:00
static int gen_pci_parse_request_of_pci_ranges ( struct device * dev ,
struct list_head * resources , struct resource * * bus_range )
2016-03-12 00:35:55 +03:00
{
int err , res_valid = 0 ;
struct device_node * np = dev - > of_node ;
resource_size_t iobase ;
2016-08-15 19:50:45 +03:00
struct resource_entry * win , * tmp ;
2016-03-12 00:35:55 +03:00
2016-05-12 01:34:46 +03:00
err = of_pci_get_host_bridge_resources ( np , 0 , 0xff , resources , & iobase ) ;
2016-03-12 00:35:55 +03:00
if ( err )
return err ;
2016-05-31 20:05:05 +03:00
err = devm_request_pci_bus_resources ( dev , resources ) ;
if ( err )
2016-05-29 02:28:51 +03:00
return err ;
2016-05-31 20:05:05 +03:00
2016-08-15 19:50:45 +03:00
resource_list_for_each_entry_safe ( win , tmp , resources ) {
2016-05-31 20:05:05 +03:00
struct resource * res = win - > res ;
2016-03-12 00:35:55 +03:00
switch ( resource_type ( res ) ) {
case IORESOURCE_IO :
err = pci_remap_iospace ( res , iobase ) ;
2016-08-15 19:50:45 +03:00
if ( err ) {
2016-03-12 00:35:55 +03:00
dev_warn ( dev , " error %d: failed to map resource %pR \n " ,
err , res ) ;
2016-08-15 19:50:45 +03:00
resource_list_destroy_entry ( win ) ;
}
2016-03-12 00:35:55 +03:00
break ;
case IORESOURCE_MEM :
res_valid | = ! ( res - > flags & IORESOURCE_PREFETCH ) ;
break ;
case IORESOURCE_BUS :
2016-05-12 01:34:46 +03:00
* bus_range = res ;
2016-05-29 02:28:51 +03:00
break ;
2016-03-12 00:35:55 +03:00
}
}
2016-05-29 02:28:51 +03:00
if ( res_valid )
return 0 ;
2016-03-12 00:35:55 +03:00
2016-05-29 02:28:51 +03:00
dev_err ( dev , " non-prefetchable memory resource required \n " ) ;
return - EINVAL ;
2016-03-12 00:35:55 +03:00
}
2016-05-12 01:34:46 +03:00
static void gen_pci_unmap_cfg ( void * ptr )
{
pci_ecam_free ( ( struct pci_config_window * ) ptr ) ;
}
static struct pci_config_window * gen_pci_init ( struct device * dev ,
struct list_head * resources , struct pci_ecam_ops * ops )
2016-03-12 00:35:55 +03:00
{
int err ;
2016-05-12 01:34:46 +03:00
struct resource cfgres ;
struct resource * bus_range = NULL ;
struct pci_config_window * cfg ;
2016-03-12 00:35:55 +03:00
2016-05-12 01:34:46 +03:00
/* Parse our PCI ranges and request their resources */
err = gen_pci_parse_request_of_pci_ranges ( dev , resources , & bus_range ) ;
if ( err )
goto err_out ;
err = of_address_to_resource ( dev - > of_node , 0 , & cfgres ) ;
2016-03-12 00:35:55 +03:00
if ( err ) {
dev_err ( dev , " missing \" reg \" property \n " ) ;
2016-05-12 01:34:46 +03:00
goto err_out ;
2016-03-12 00:35:55 +03:00
}
2016-05-12 01:34:46 +03:00
cfg = pci_ecam_create ( dev , & cfgres , bus_range , ops ) ;
if ( IS_ERR ( cfg ) ) {
err = PTR_ERR ( cfg ) ;
goto err_out ;
2016-03-12 00:35:55 +03:00
}
2016-05-12 01:34:46 +03:00
err = devm_add_action ( dev , gen_pci_unmap_cfg , cfg ) ;
if ( err ) {
gen_pci_unmap_cfg ( cfg ) ;
goto err_out ;
}
return cfg ;
err_out :
pci_free_resource_list ( resources ) ;
return ERR_PTR ( err ) ;
2016-03-12 00:35:55 +03:00
}
int pci_host_common_probe ( struct platform_device * pdev ,
2016-05-12 01:34:46 +03:00
struct pci_ecam_ops * ops )
2016-03-12 00:35:55 +03:00
{
const char * type ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct pci_bus * bus , * child ;
2016-05-12 01:34:46 +03:00
struct pci_config_window * cfg ;
struct list_head resources ;
2016-03-12 00:35:55 +03:00
type = of_get_property ( np , " device_type " , NULL ) ;
if ( ! type | | strcmp ( type , " pci " ) ) {
dev_err ( dev , " invalid \" device_type \" %s \n " , type ) ;
return - EINVAL ;
}
of_pci_check_probe_only ( ) ;
/* Parse and map our Configuration Space windows */
2016-05-12 01:34:46 +03:00
INIT_LIST_HEAD ( & resources ) ;
cfg = gen_pci_init ( dev , & resources , ops ) ;
if ( IS_ERR ( cfg ) )
return PTR_ERR ( cfg ) ;
2016-03-12 00:35:55 +03:00
/* Do not reassign resources if probe only */
if ( ! pci_has_flag ( PCI_PROBE_ONLY ) )
pci_add_flags ( PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS ) ;
2016-05-12 01:34:46 +03:00
bus = pci_scan_root_bus ( dev , cfg - > busr . start , & ops - > pci_ops , cfg ,
& resources ) ;
2016-03-12 00:35:55 +03:00
if ( ! bus ) {
dev_err ( dev , " Scanning rootbus failed " ) ;
return - ENODEV ;
}
PCI: generic: Call pci_fixup_irqs() only on ARM
pci_fixup_irqs() is problematic because:
- it's called when we enumerate a host bridge, so we don't fixup IRQs for
hot-added PCI devices, and
- it fixes up IRQs for all PCI devices in the system, so if we call it
multiple times, e.g., if we have several host controllers, we may
reallocate an IRQ for a device after a driver has already claimed it.
We plan to replace pci_fixup_irqs() soon, but we still need it on ARM
because we don't have any other generic method for doing this.
On ARM64, we don't need pci_fixup_irqs() because we do IRQ setup when we
bind a driver to the device (in the pci_device_probe() ->
pcibios_alloc_irq() path).
pci-host-common.c is currently only used on ARM and ARM64. In principle,
it could be used on x86, and we wouldn't want pci_fixup_irqs() there
either, because x86 does IRQ setup in the pci_enable_device() path.
[bhelgaas: changelog, use #ifdef ARM, not #ifndef ARM64]
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Reviewed-by: Zhou Wang <wangzhou1@hisilicon.com>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2017-01-12 09:28:24 +03:00
# ifdef CONFIG_ARM
2016-03-12 00:35:55 +03:00
pci_fixup_irqs ( pci_common_swizzle , of_irq_parse_and_map_pci ) ;
PCI: generic: Call pci_fixup_irqs() only on ARM
pci_fixup_irqs() is problematic because:
- it's called when we enumerate a host bridge, so we don't fixup IRQs for
hot-added PCI devices, and
- it fixes up IRQs for all PCI devices in the system, so if we call it
multiple times, e.g., if we have several host controllers, we may
reallocate an IRQ for a device after a driver has already claimed it.
We plan to replace pci_fixup_irqs() soon, but we still need it on ARM
because we don't have any other generic method for doing this.
On ARM64, we don't need pci_fixup_irqs() because we do IRQ setup when we
bind a driver to the device (in the pci_device_probe() ->
pcibios_alloc_irq() path).
pci-host-common.c is currently only used on ARM and ARM64. In principle,
it could be used on x86, and we wouldn't want pci_fixup_irqs() there
either, because x86 does IRQ setup in the pci_enable_device() path.
[bhelgaas: changelog, use #ifdef ARM, not #ifndef ARM64]
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
Reviewed-by: Zhou Wang <wangzhou1@hisilicon.com>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2017-01-12 09:28:24 +03:00
# endif
2016-03-12 00:35:55 +03:00
PCI: generic: Claim bus resources on PCI_PROBE_ONLY set-ups
We claim PCI BAR and bridge window resources in pci_bus_assign_resources(),
but when PCI_PROBE_ONLY is set, we treat those resources as immutable and
don't call pci_bus_assign_resources(), so the resources aren't put in the
resource tree.
When the resources aren't in the tree, they don't show up in /proc/iomem,
we can't detect conflicts, and we need special cases elsewhere for
PCI_PROBE_ONLY or resources without a parent pointer.
Claim all PCI BAR and window resources in the PCI_PROBE_ONLY case.
If a PCI_PROBE_ONLY platform assigns conflicting resources, Linux can't fix
the conflicts. Previously we didn't notice the conflicts, but now we will,
which may expose new failures.
[bhelgaas: changelog, summarize comment]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Will Deacon <will.deacon@arm.com>
CC: Arnd Bergmann <arnd@arndb.de>
CC: David Daney <david.daney@cavium.com>
2016-06-08 14:04:48 +03:00
/*
* We insert PCI resources into the iomem_resource and
* ioport_resource trees in either pci_bus_claim_resources ( )
* or pci_bus_assign_resources ( ) .
*/
if ( pci_has_flag ( PCI_PROBE_ONLY ) ) {
pci_bus_claim_resources ( bus ) ;
} else {
2016-03-12 00:35:55 +03:00
pci_bus_size_bridges ( bus ) ;
pci_bus_assign_resources ( bus ) ;
list_for_each_entry ( child , & bus - > children , node )
pcie_bus_configure_settings ( child ) ;
}
pci_bus_add_devices ( bus ) ;
return 0 ;
}