162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Host bridge related code
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "pci.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic struct pci_bus *find_pci_root_bus(struct pci_bus *bus)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	while (bus->parent)
1562306a36Sopenharmony_ci		bus = bus->parent;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	return bus;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct pci_bus *root_bus = find_pci_root_bus(bus);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	return to_pci_host_bridge(root_bus->bridge);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_find_host_bridge);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct device *pci_get_host_bridge_device(struct pci_dev *dev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct pci_bus *root_bus = find_pci_root_bus(dev->bus);
3162306a36Sopenharmony_ci	struct device *bridge = root_bus->bridge;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	kobject_get(&bridge->kobj);
3462306a36Sopenharmony_ci	return bridge;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid  pci_put_host_bridge_device(struct device *dev)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	kobject_put(&dev->kobj);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_civoid pci_set_host_bridge_release(struct pci_host_bridge *bridge,
4362306a36Sopenharmony_ci				 void (*release_fn)(struct pci_host_bridge *),
4462306a36Sopenharmony_ci				 void *release_data)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	bridge->release_fn = release_fn;
4762306a36Sopenharmony_ci	bridge->release_data = release_data;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_set_host_bridge_release);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
5262306a36Sopenharmony_ci			     struct resource *res)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
5562306a36Sopenharmony_ci	struct resource_entry *window;
5662306a36Sopenharmony_ci	resource_size_t offset = 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	resource_list_for_each_entry(window, &bridge->windows) {
5962306a36Sopenharmony_ci		if (resource_contains(window->res, res)) {
6062306a36Sopenharmony_ci			offset = window->offset;
6162306a36Sopenharmony_ci			break;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	region->start = res->start - offset;
6662306a36Sopenharmony_ci	region->end = res->end - offset;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ciEXPORT_SYMBOL(pcibios_resource_to_bus);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic bool region_contains(struct pci_bus_region *region1,
7162306a36Sopenharmony_ci			    struct pci_bus_region *region2)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return region1->start <= region2->start && region1->end >= region2->end;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
7762306a36Sopenharmony_ci			     struct pci_bus_region *region)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
8062306a36Sopenharmony_ci	struct resource_entry *window;
8162306a36Sopenharmony_ci	resource_size_t offset = 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	resource_list_for_each_entry(window, &bridge->windows) {
8462306a36Sopenharmony_ci		struct pci_bus_region bus_region;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		if (resource_type(res) != resource_type(window->res))
8762306a36Sopenharmony_ci			continue;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		bus_region.start = window->res->start - window->offset;
9062306a36Sopenharmony_ci		bus_region.end = window->res->end - window->offset;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		if (region_contains(&bus_region, region)) {
9362306a36Sopenharmony_ci			offset = window->offset;
9462306a36Sopenharmony_ci			break;
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	res->start = region->start + offset;
9962306a36Sopenharmony_ci	res->end = region->end + offset;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciEXPORT_SYMBOL(pcibios_bus_to_resource);
102