pci-host-common.c 4.1 KB
Newer Older
1
/*
2 3
 * Generic PCI host driver common code
 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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>
24
#include <linux/pci-ecam.h>
25 26
#include <linux/platform_device.h>

27 28
static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
		       struct list_head *resources, struct resource **bus_range)
29 30 31 32 33 34
{
	int err, res_valid = 0;
	struct device_node *np = dev->of_node;
	resource_size_t iobase;
	struct resource_entry *win;

35
	err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
36 37 38
	if (err)
		return err;

39 40
	err = devm_request_pci_bus_resources(dev, resources);
	if (err)
41
		return err;
42

43
	resource_list_for_each_entry(win, resources) {
44
		struct resource *res = win->res;
45 46 47 48

		switch (resource_type(res)) {
		case IORESOURCE_IO:
			err = pci_remap_iospace(res, iobase);
49
			if (err)
50 51 52 53 54 55 56
				dev_warn(dev, "error %d: failed to map resource %pR\n",
					 err, res);
			break;
		case IORESOURCE_MEM:
			res_valid |= !(res->flags & IORESOURCE_PREFETCH);
			break;
		case IORESOURCE_BUS:
57
			*bus_range = res;
58
			break;
59 60 61
		}
	}

62 63
	if (res_valid)
		return 0;
64

65 66
	dev_err(dev, "non-prefetchable memory resource required\n");
	return -EINVAL;
67 68
}

69 70 71 72 73 74 75
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)
76 77
{
	int err;
78 79 80
	struct resource cfgres;
	struct resource *bus_range = NULL;
	struct pci_config_window *cfg;
81

82 83 84 85 86 87
	/* 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);
88 89
	if (err) {
		dev_err(dev, "missing \"reg\" property\n");
90
		goto err_out;
91 92
	}

93 94 95 96
	cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
	if (IS_ERR(cfg)) {
		err = PTR_ERR(cfg);
		goto err_out;
97 98
	}

99 100 101 102 103 104 105 106 107 108
	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);
109 110 111
}

int pci_host_common_probe(struct platform_device *pdev,
112
			  struct pci_ecam_ops *ops)
113 114 115 116 117
{
	const char *type;
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	struct pci_bus *bus, *child;
118 119
	struct pci_config_window *cfg;
	struct list_head resources;
120 121 122 123 124 125 126 127 128 129

	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 */
130 131 132 133
	INIT_LIST_HEAD(&resources);
	cfg = gen_pci_init(dev, &resources, ops);
	if (IS_ERR(cfg))
		return PTR_ERR(cfg);
134 135 136 137 138

	/* 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);

139 140
	bus = pci_scan_root_bus(dev, cfg->busr.start, &ops->pci_ops, cfg,
				&resources);
141 142 143 144 145 146 147
	if (!bus) {
		dev_err(dev, "Scanning rootbus failed");
		return -ENODEV;
	}

	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);

148 149 150 151 152 153 154 155
	/*
	 * 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 {
156 157 158 159 160 161 162 163 164 165
		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;
}