xref: /linux/drivers/pci/controller/pci-host-common.c (revision e63434f4cc0da0f3ffe60d932997842f60552517)
16e0832faSShawn Lin // SPDX-License-Identifier: GPL-2.0
26e0832faSShawn Lin /*
36e0832faSShawn Lin  * Generic PCI host driver common code
46e0832faSShawn Lin  *
56e0832faSShawn Lin  * Copyright (C) 2014 ARM Limited
66e0832faSShawn Lin  *
76e0832faSShawn Lin  * Author: Will Deacon <will.deacon@arm.com>
86e0832faSShawn Lin  */
96e0832faSShawn Lin 
106e0832faSShawn Lin #include <linux/kernel.h>
110c59c06aSRob Herring #include <linux/module.h>
126e0832faSShawn Lin #include <linux/of_address.h>
13b2f75a41SRob Herring #include <linux/of_device.h>
146e0832faSShawn Lin #include <linux/of_pci.h>
156e0832faSShawn Lin #include <linux/pci-ecam.h>
166e0832faSShawn Lin #include <linux/platform_device.h>
176e0832faSShawn Lin 
186e0832faSShawn Lin static void gen_pci_unmap_cfg(void *ptr)
196e0832faSShawn Lin {
206e0832faSShawn Lin 	pci_ecam_free((struct pci_config_window *)ptr);
216e0832faSShawn Lin }
226e0832faSShawn Lin 
236e0832faSShawn Lin static struct pci_config_window *gen_pci_init(struct device *dev,
24*e63434f4SRob Herring 		struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops)
256e0832faSShawn Lin {
266e0832faSShawn Lin 	int err;
276e0832faSShawn Lin 	struct resource cfgres;
286e0832faSShawn Lin 	struct resource *bus_range = NULL;
296e0832faSShawn Lin 	struct pci_config_window *cfg;
306e0832faSShawn Lin 
316e0832faSShawn Lin 	/* Parse our PCI ranges and request their resources */
32*e63434f4SRob Herring 	err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, &bus_range);
336e0832faSShawn Lin 	if (err)
346e0832faSShawn Lin 		return ERR_PTR(err);
356e0832faSShawn Lin 
366e0832faSShawn Lin 	err = of_address_to_resource(dev->of_node, 0, &cfgres);
376e0832faSShawn Lin 	if (err) {
386e0832faSShawn Lin 		dev_err(dev, "missing \"reg\" property\n");
39*e63434f4SRob Herring 		return ERR_PTR(err);
406e0832faSShawn Lin 	}
416e0832faSShawn Lin 
426e0832faSShawn Lin 	cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
43*e63434f4SRob Herring 	if (IS_ERR(cfg))
446e0832faSShawn Lin 		return cfg;
456e0832faSShawn Lin 
46*e63434f4SRob Herring 	err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
47*e63434f4SRob Herring 	if (err)
486e0832faSShawn Lin 		return ERR_PTR(err);
49*e63434f4SRob Herring 
50*e63434f4SRob Herring 	return cfg;
516e0832faSShawn Lin }
526e0832faSShawn Lin 
53b2f75a41SRob Herring int pci_host_common_probe(struct platform_device *pdev)
546e0832faSShawn Lin {
556e0832faSShawn Lin 	struct device *dev = &pdev->dev;
566e0832faSShawn Lin 	struct pci_host_bridge *bridge;
576e0832faSShawn Lin 	struct pci_config_window *cfg;
58b2f75a41SRob Herring 	const struct pci_ecam_ops *ops;
596e0832faSShawn Lin 
60b2f75a41SRob Herring 	ops = of_device_get_match_data(&pdev->dev);
61b2f75a41SRob Herring 	if (!ops)
62b2f75a41SRob Herring 		return -ENODEV;
63b2f75a41SRob Herring 
646e0832faSShawn Lin 	bridge = devm_pci_alloc_host_bridge(dev, 0);
656e0832faSShawn Lin 	if (!bridge)
666e0832faSShawn Lin 		return -ENOMEM;
676e0832faSShawn Lin 
686e0832faSShawn Lin 	of_pci_check_probe_only();
696e0832faSShawn Lin 
706e0832faSShawn Lin 	/* Parse and map our Configuration Space windows */
71*e63434f4SRob Herring 	cfg = gen_pci_init(dev, bridge, ops);
726e0832faSShawn Lin 	if (IS_ERR(cfg))
736e0832faSShawn Lin 		return PTR_ERR(cfg);
746e0832faSShawn Lin 
756e0832faSShawn Lin 	/* Do not reassign resources if probe only */
766e0832faSShawn Lin 	if (!pci_has_flag(PCI_PROBE_ONLY))
776e0832faSShawn Lin 		pci_add_flags(PCI_REASSIGN_ALL_BUS);
786e0832faSShawn Lin 
796e0832faSShawn Lin 	bridge->dev.parent = dev;
806e0832faSShawn Lin 	bridge->sysdata = cfg;
816e0832faSShawn Lin 	bridge->busnr = cfg->busr.start;
820b104773SRob Herring 	bridge->ops = (struct pci_ops *)&ops->pci_ops;
836e0832faSShawn Lin 	bridge->map_irq = of_irq_parse_and_map_pci;
846e0832faSShawn Lin 	bridge->swizzle_irq = pci_common_swizzle;
856e0832faSShawn Lin 
86*e63434f4SRob Herring 	platform_set_drvdata(pdev, bridge);
876e0832faSShawn Lin 
88*e63434f4SRob Herring 	return pci_host_probe(bridge);
896e0832faSShawn Lin }
900c59c06aSRob Herring EXPORT_SYMBOL_GPL(pci_host_common_probe);
916e0832faSShawn Lin 
926e0832faSShawn Lin int pci_host_common_remove(struct platform_device *pdev)
936e0832faSShawn Lin {
94*e63434f4SRob Herring 	struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
956e0832faSShawn Lin 
966e0832faSShawn Lin 	pci_lock_rescan_remove();
97*e63434f4SRob Herring 	pci_stop_root_bus(bridge->bus);
98*e63434f4SRob Herring 	pci_remove_root_bus(bridge->bus);
996e0832faSShawn Lin 	pci_unlock_rescan_remove();
1006e0832faSShawn Lin 
1016e0832faSShawn Lin 	return 0;
1026e0832faSShawn Lin }
1030c59c06aSRob Herring EXPORT_SYMBOL_GPL(pci_host_common_remove);
1040c59c06aSRob Herring 
1050c59c06aSRob Herring MODULE_LICENSE("GPL v2");
106