162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI <-> OF mapping helpers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011 IBM Corp. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define pr_fmt(fmt) "PCI: OF: " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/irqdomain.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_irq.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/of_pci.h> 1662306a36Sopenharmony_ci#include "pci.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#ifdef CONFIG_PCI 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * pci_set_of_node - Find and set device's DT device_node 2162306a36Sopenharmony_ci * @dev: the PCI device structure to fill 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Returns 0 on success with of_node set or when no device is described in the 2462306a36Sopenharmony_ci * DT. Returns -ENODEV if the device is present, but disabled in the DT. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ciint pci_set_of_node(struct pci_dev *dev) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct device_node *node; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (!dev->bus->dev.of_node) 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci node = of_pci_find_child_device(dev->bus->dev.of_node, dev->devfn); 3462306a36Sopenharmony_ci if (!node) 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci device_set_node(&dev->dev, of_fwnode_handle(node)); 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_civoid pci_release_of_node(struct pci_dev *dev) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci of_node_put(dev->dev.of_node); 4462306a36Sopenharmony_ci device_set_node(&dev->dev, NULL); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_civoid pci_set_bus_of_node(struct pci_bus *bus) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct device_node *node; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (bus->self == NULL) { 5262306a36Sopenharmony_ci node = pcibios_get_phb_of_node(bus); 5362306a36Sopenharmony_ci } else { 5462306a36Sopenharmony_ci node = of_node_get(bus->self->dev.of_node); 5562306a36Sopenharmony_ci if (node && of_property_read_bool(node, "external-facing")) 5662306a36Sopenharmony_ci bus->self->external_facing = true; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci device_set_node(&bus->dev, of_fwnode_handle(node)); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_civoid pci_release_bus_of_node(struct pci_bus *bus) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci of_node_put(bus->dev.of_node); 6562306a36Sopenharmony_ci device_set_node(&bus->dev, NULL); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci /* This should only be called for PHBs */ 7162306a36Sopenharmony_ci if (WARN_ON(bus->self || bus->parent)) 7262306a36Sopenharmony_ci return NULL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * Look for a node pointer in either the intermediary device we 7662306a36Sopenharmony_ci * create above the root bus or its own parent. Normally only 7762306a36Sopenharmony_ci * the later is populated. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci if (bus->bridge->of_node) 8062306a36Sopenharmony_ci return of_node_get(bus->bridge->of_node); 8162306a36Sopenharmony_ci if (bus->bridge->parent && bus->bridge->parent->of_node) 8262306a36Sopenharmony_ci return of_node_get(bus->bridge->parent->of_node); 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci#ifdef CONFIG_IRQ_DOMAIN 8962306a36Sopenharmony_ci struct irq_domain *d; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!bus->dev.of_node) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Start looking for a phandle to an MSI controller. */ 9562306a36Sopenharmony_ci d = of_msi_get_domain(&bus->dev, bus->dev.of_node, DOMAIN_BUS_PCI_MSI); 9662306a36Sopenharmony_ci if (d) 9762306a36Sopenharmony_ci return d; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * If we don't have an msi-parent property, look for a domain 10162306a36Sopenharmony_ci * directly attached to the host bridge. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci d = irq_find_matching_host(bus->dev.of_node, DOMAIN_BUS_PCI_MSI); 10462306a36Sopenharmony_ci if (d) 10562306a36Sopenharmony_ci return d; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return irq_find_host(bus->dev.of_node); 10862306a36Sopenharmony_ci#else 10962306a36Sopenharmony_ci return NULL; 11062306a36Sopenharmony_ci#endif 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cibool pci_host_of_has_msi_map(struct device *dev) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci if (dev && dev->of_node) 11662306a36Sopenharmony_ci return of_get_property(dev->of_node, "msi-map", NULL); 11762306a36Sopenharmony_ci return false; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic inline int __of_pci_pci_compare(struct device_node *node, 12162306a36Sopenharmony_ci unsigned int data) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int devfn; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci devfn = of_pci_get_devfn(node); 12662306a36Sopenharmony_ci if (devfn < 0) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return devfn == data; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct device_node *of_pci_find_child_device(struct device_node *parent, 13362306a36Sopenharmony_ci unsigned int devfn) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct device_node *node, *node2; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for_each_child_of_node(parent, node) { 13862306a36Sopenharmony_ci if (__of_pci_pci_compare(node, devfn)) 13962306a36Sopenharmony_ci return node; 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * Some OFs create a parent node "multifunc-device" as 14262306a36Sopenharmony_ci * a fake root for all functions of a multi-function 14362306a36Sopenharmony_ci * device we go down them as well. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci if (of_node_name_eq(node, "multifunc-device")) { 14662306a36Sopenharmony_ci for_each_child_of_node(node, node2) { 14762306a36Sopenharmony_ci if (__of_pci_pci_compare(node2, devfn)) { 14862306a36Sopenharmony_ci of_node_put(node); 14962306a36Sopenharmony_ci return node2; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_find_child_device); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * of_pci_get_devfn() - Get device and function numbers for a device node 16062306a36Sopenharmony_ci * @np: device node 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Parses a standard 5-cell PCI resource and returns an 8-bit value that can 16362306a36Sopenharmony_ci * be passed to the PCI_SLOT() and PCI_FUNC() macros to extract the device 16462306a36Sopenharmony_ci * and function numbers respectively. On error a negative error code is 16562306a36Sopenharmony_ci * returned. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ciint of_pci_get_devfn(struct device_node *np) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 reg[5]; 17062306a36Sopenharmony_ci int error; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci error = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg)); 17362306a36Sopenharmony_ci if (error) 17462306a36Sopenharmony_ci return error; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return (reg[0] >> 8) & 0xff; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_get_devfn); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * of_pci_parse_bus_range() - parse the bus-range property of a PCI device 18262306a36Sopenharmony_ci * @node: device node 18362306a36Sopenharmony_ci * @res: address to a struct resource to return the bus-range 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * Returns 0 on success or a negative error-code on failure. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ciint of_pci_parse_bus_range(struct device_node *node, struct resource *res) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci u32 bus_range[2]; 19062306a36Sopenharmony_ci int error; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci error = of_property_read_u32_array(node, "bus-range", bus_range, 19362306a36Sopenharmony_ci ARRAY_SIZE(bus_range)); 19462306a36Sopenharmony_ci if (error) 19562306a36Sopenharmony_ci return error; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci res->name = node->name; 19862306a36Sopenharmony_ci res->start = bus_range[0]; 19962306a36Sopenharmony_ci res->end = bus_range[1]; 20062306a36Sopenharmony_ci res->flags = IORESOURCE_BUS; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_parse_bus_range); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/** 20762306a36Sopenharmony_ci * of_get_pci_domain_nr - Find the host bridge domain number 20862306a36Sopenharmony_ci * of the given device node. 20962306a36Sopenharmony_ci * @node: Device tree node with the domain information. 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * This function will try to obtain the host bridge domain number by finding 21262306a36Sopenharmony_ci * a property called "linux,pci-domain" of the given device node. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Return: 21562306a36Sopenharmony_ci * * > 0 - On success, an associated domain number. 21662306a36Sopenharmony_ci * * -EINVAL - The property "linux,pci-domain" does not exist. 21762306a36Sopenharmony_ci * * -ENODATA - The linux,pci-domain" property does not have value. 21862306a36Sopenharmony_ci * * -EOVERFLOW - Invalid "linux,pci-domain" property value. 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Returns the associated domain number from DT in the range [0-0xffff], or 22162306a36Sopenharmony_ci * a negative value if the required property is not found. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ciint of_get_pci_domain_nr(struct device_node *node) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci u32 domain; 22662306a36Sopenharmony_ci int error; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci error = of_property_read_u32(node, "linux,pci-domain", &domain); 22962306a36Sopenharmony_ci if (error) 23062306a36Sopenharmony_ci return error; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return (u16)domain; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_get_pci_domain_nr); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/** 23762306a36Sopenharmony_ci * of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only 23862306a36Sopenharmony_ci * is present and valid 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_civoid of_pci_check_probe_only(void) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci u32 val; 24362306a36Sopenharmony_ci int ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = of_property_read_u32(of_chosen, "linux,pci-probe-only", &val); 24662306a36Sopenharmony_ci if (ret) { 24762306a36Sopenharmony_ci if (ret == -ENODATA || ret == -EOVERFLOW) 24862306a36Sopenharmony_ci pr_warn("linux,pci-probe-only without valid value, ignoring\n"); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (val) 25362306a36Sopenharmony_ci pci_add_flags(PCI_PROBE_ONLY); 25462306a36Sopenharmony_ci else 25562306a36Sopenharmony_ci pci_clear_flags(PCI_PROBE_ONLY); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci pr_info("PROBE_ONLY %s\n", val ? "enabled" : "disabled"); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_check_probe_only); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/** 26262306a36Sopenharmony_ci * devm_of_pci_get_host_bridge_resources() - Resource-managed parsing of PCI 26362306a36Sopenharmony_ci * host bridge resources from DT 26462306a36Sopenharmony_ci * @dev: host bridge device 26562306a36Sopenharmony_ci * @busno: bus number associated with the bridge root bus 26662306a36Sopenharmony_ci * @bus_max: maximum number of buses for this bridge 26762306a36Sopenharmony_ci * @resources: list where the range of resources will be added after DT parsing 26862306a36Sopenharmony_ci * @ib_resources: list where the range of inbound resources (with addresses 26962306a36Sopenharmony_ci * from 'dma-ranges') will be added after DT parsing 27062306a36Sopenharmony_ci * @io_base: pointer to a variable that will contain on return the physical 27162306a36Sopenharmony_ci * address for the start of the I/O range. Can be NULL if the caller doesn't 27262306a36Sopenharmony_ci * expect I/O ranges to be present in the device tree. 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * This function will parse the "ranges" property of a PCI host bridge device 27562306a36Sopenharmony_ci * node and setup the resource mapping based on its content. It is expected 27662306a36Sopenharmony_ci * that the property conforms with the Power ePAPR document. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * It returns zero if the range parsing has been successful or a standard error 27962306a36Sopenharmony_ci * value if it failed. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic int devm_of_pci_get_host_bridge_resources(struct device *dev, 28262306a36Sopenharmony_ci unsigned char busno, unsigned char bus_max, 28362306a36Sopenharmony_ci struct list_head *resources, 28462306a36Sopenharmony_ci struct list_head *ib_resources, 28562306a36Sopenharmony_ci resource_size_t *io_base) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct device_node *dev_node = dev->of_node; 28862306a36Sopenharmony_ci struct resource *res, tmp_res; 28962306a36Sopenharmony_ci struct resource *bus_range; 29062306a36Sopenharmony_ci struct of_pci_range range; 29162306a36Sopenharmony_ci struct of_pci_range_parser parser; 29262306a36Sopenharmony_ci const char *range_type; 29362306a36Sopenharmony_ci int err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (io_base) 29662306a36Sopenharmony_ci *io_base = (resource_size_t)OF_BAD_ADDR; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci bus_range = devm_kzalloc(dev, sizeof(*bus_range), GFP_KERNEL); 29962306a36Sopenharmony_ci if (!bus_range) 30062306a36Sopenharmony_ci return -ENOMEM; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_info(dev, "host bridge %pOF ranges:\n", dev_node); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci err = of_pci_parse_bus_range(dev_node, bus_range); 30562306a36Sopenharmony_ci if (err) { 30662306a36Sopenharmony_ci bus_range->start = busno; 30762306a36Sopenharmony_ci bus_range->end = bus_max; 30862306a36Sopenharmony_ci bus_range->flags = IORESOURCE_BUS; 30962306a36Sopenharmony_ci dev_info(dev, " No bus range found for %pOF, using %pR\n", 31062306a36Sopenharmony_ci dev_node, bus_range); 31162306a36Sopenharmony_ci } else { 31262306a36Sopenharmony_ci if (bus_range->end > bus_range->start + bus_max) 31362306a36Sopenharmony_ci bus_range->end = bus_range->start + bus_max; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci pci_add_resource(resources, bus_range); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Check for ranges property */ 31862306a36Sopenharmony_ci err = of_pci_range_parser_init(&parser, dev_node); 31962306a36Sopenharmony_ci if (err) 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev_dbg(dev, "Parsing ranges property...\n"); 32362306a36Sopenharmony_ci for_each_of_pci_range(&parser, &range) { 32462306a36Sopenharmony_ci /* Read next ranges element */ 32562306a36Sopenharmony_ci if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) 32662306a36Sopenharmony_ci range_type = "IO"; 32762306a36Sopenharmony_ci else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) 32862306a36Sopenharmony_ci range_type = "MEM"; 32962306a36Sopenharmony_ci else 33062306a36Sopenharmony_ci range_type = "err"; 33162306a36Sopenharmony_ci dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n", 33262306a36Sopenharmony_ci range_type, range.cpu_addr, 33362306a36Sopenharmony_ci range.cpu_addr + range.size - 1, range.pci_addr); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * If we failed translation or got a zero-sized region 33762306a36Sopenharmony_ci * then skip this range 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci if (range.cpu_addr == OF_BAD_ADDR || range.size == 0) 34062306a36Sopenharmony_ci continue; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci err = of_pci_range_to_resource(&range, dev_node, &tmp_res); 34362306a36Sopenharmony_ci if (err) 34462306a36Sopenharmony_ci continue; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL); 34762306a36Sopenharmony_ci if (!res) { 34862306a36Sopenharmony_ci err = -ENOMEM; 34962306a36Sopenharmony_ci goto failed; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (resource_type(res) == IORESOURCE_IO) { 35362306a36Sopenharmony_ci if (!io_base) { 35462306a36Sopenharmony_ci dev_err(dev, "I/O range found for %pOF. Please provide an io_base pointer to save CPU base address\n", 35562306a36Sopenharmony_ci dev_node); 35662306a36Sopenharmony_ci err = -EINVAL; 35762306a36Sopenharmony_ci goto failed; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci if (*io_base != (resource_size_t)OF_BAD_ADDR) 36062306a36Sopenharmony_ci dev_warn(dev, "More than one I/O resource converted for %pOF. CPU base address for old range lost!\n", 36162306a36Sopenharmony_ci dev_node); 36262306a36Sopenharmony_ci *io_base = range.cpu_addr; 36362306a36Sopenharmony_ci } else if (resource_type(res) == IORESOURCE_MEM) { 36462306a36Sopenharmony_ci res->flags &= ~IORESOURCE_MEM_64; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci pci_add_resource_offset(resources, res, res->start - range.pci_addr); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Check for dma-ranges property */ 37162306a36Sopenharmony_ci if (!ib_resources) 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci err = of_pci_dma_range_parser_init(&parser, dev_node); 37462306a36Sopenharmony_ci if (err) 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dev_dbg(dev, "Parsing dma-ranges property...\n"); 37862306a36Sopenharmony_ci for_each_of_pci_range(&parser, &range) { 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * If we failed translation or got a zero-sized region 38162306a36Sopenharmony_ci * then skip this range 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) || 38462306a36Sopenharmony_ci range.cpu_addr == OF_BAD_ADDR || range.size == 0) 38562306a36Sopenharmony_ci continue; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n", 38862306a36Sopenharmony_ci "IB MEM", range.cpu_addr, 38962306a36Sopenharmony_ci range.cpu_addr + range.size - 1, range.pci_addr); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci err = of_pci_range_to_resource(&range, dev_node, &tmp_res); 39362306a36Sopenharmony_ci if (err) 39462306a36Sopenharmony_ci continue; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL); 39762306a36Sopenharmony_ci if (!res) { 39862306a36Sopenharmony_ci err = -ENOMEM; 39962306a36Sopenharmony_ci goto failed; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci pci_add_resource_offset(ib_resources, res, 40362306a36Sopenharmony_ci res->start - range.pci_addr); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cifailed: 40962306a36Sopenharmony_ci pci_free_resource_list(resources); 41062306a36Sopenharmony_ci return err; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_IRQ) 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * of_irq_parse_pci - Resolve the interrupt for a PCI device 41662306a36Sopenharmony_ci * @pdev: the device whose interrupt is to be resolved 41762306a36Sopenharmony_ci * @out_irq: structure of_phandle_args filled by this function 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * This function resolves the PCI interrupt for a given PCI device. If a 42062306a36Sopenharmony_ci * device-node exists for a given pci_dev, it will use normal OF tree 42162306a36Sopenharmony_ci * walking. If not, it will implement standard swizzling and walk up the 42262306a36Sopenharmony_ci * PCI tree until an device-node is found, at which point it will finish 42362306a36Sopenharmony_ci * resolving using the OF tree walking. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct device_node *dn, *ppnode = NULL; 42862306a36Sopenharmony_ci struct pci_dev *ppdev; 42962306a36Sopenharmony_ci __be32 laddr[3]; 43062306a36Sopenharmony_ci u8 pin; 43162306a36Sopenharmony_ci int rc; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Check if we have a device node, if yes, fallback to standard 43562306a36Sopenharmony_ci * device tree parsing 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci dn = pci_device_to_OF_node(pdev); 43862306a36Sopenharmony_ci if (dn) { 43962306a36Sopenharmony_ci rc = of_irq_parse_one(dn, 0, out_irq); 44062306a36Sopenharmony_ci if (!rc) 44162306a36Sopenharmony_ci return rc; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * Ok, we don't, time to have fun. Let's start by building up an 44662306a36Sopenharmony_ci * interrupt spec. we assume #interrupt-cells is 1, which is standard 44762306a36Sopenharmony_ci * for PCI. If you do different, then don't use that routine. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); 45062306a36Sopenharmony_ci if (rc != 0) 45162306a36Sopenharmony_ci goto err; 45262306a36Sopenharmony_ci /* No pin, exit with no error message. */ 45362306a36Sopenharmony_ci if (pin == 0) 45462306a36Sopenharmony_ci return -ENODEV; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Local interrupt-map in the device node? Use it! */ 45762306a36Sopenharmony_ci if (of_property_present(dn, "interrupt-map")) { 45862306a36Sopenharmony_ci pin = pci_swizzle_interrupt_pin(pdev, pin); 45962306a36Sopenharmony_ci ppnode = dn; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Now we walk up the PCI tree */ 46362306a36Sopenharmony_ci while (!ppnode) { 46462306a36Sopenharmony_ci /* Get the pci_dev of our parent */ 46562306a36Sopenharmony_ci ppdev = pdev->bus->self; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Ouch, it's a host bridge... */ 46862306a36Sopenharmony_ci if (ppdev == NULL) { 46962306a36Sopenharmony_ci ppnode = pci_bus_to_OF_node(pdev->bus); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* No node for host bridge ? give up */ 47262306a36Sopenharmony_ci if (ppnode == NULL) { 47362306a36Sopenharmony_ci rc = -EINVAL; 47462306a36Sopenharmony_ci goto err; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci /* We found a P2P bridge, check if it has a node */ 47862306a36Sopenharmony_ci ppnode = pci_device_to_OF_node(ppdev); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * Ok, we have found a parent with a device-node, hand over to 48362306a36Sopenharmony_ci * the OF parsing code. 48462306a36Sopenharmony_ci * We build a unit address from the linux device to be used for 48562306a36Sopenharmony_ci * resolution. Note that we use the linux bus number which may 48662306a36Sopenharmony_ci * not match your firmware bus numbering. 48762306a36Sopenharmony_ci * Fortunately, in most cases, interrupt-map-mask doesn't 48862306a36Sopenharmony_ci * include the bus number as part of the matching. 48962306a36Sopenharmony_ci * You should still be careful about that though if you intend 49062306a36Sopenharmony_ci * to rely on this function (you ship a firmware that doesn't 49162306a36Sopenharmony_ci * create device nodes for all PCI devices). 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci if (ppnode) 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * We can only get here if we hit a P2P bridge with no node; 49862306a36Sopenharmony_ci * let's do standard swizzling and try again 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci pin = pci_swizzle_interrupt_pin(pdev, pin); 50162306a36Sopenharmony_ci pdev = ppdev; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci out_irq->np = ppnode; 50562306a36Sopenharmony_ci out_irq->args_count = 1; 50662306a36Sopenharmony_ci out_irq->args[0] = pin; 50762306a36Sopenharmony_ci laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); 50862306a36Sopenharmony_ci laddr[1] = laddr[2] = cpu_to_be32(0); 50962306a36Sopenharmony_ci rc = of_irq_parse_raw(laddr, out_irq); 51062306a36Sopenharmony_ci if (rc) 51162306a36Sopenharmony_ci goto err; 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_cierr: 51462306a36Sopenharmony_ci if (rc == -ENOENT) { 51562306a36Sopenharmony_ci dev_warn(&pdev->dev, 51662306a36Sopenharmony_ci "%s: no interrupt-map found, INTx interrupts not available\n", 51762306a36Sopenharmony_ci __func__); 51862306a36Sopenharmony_ci pr_warn_once("%s: possibly some PCI slots don't have level triggered interrupts capability\n", 51962306a36Sopenharmony_ci __func__); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci dev_err(&pdev->dev, "%s: failed with rc=%d\n", __func__, rc); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci return rc; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/** 52762306a36Sopenharmony_ci * of_irq_parse_and_map_pci() - Decode a PCI IRQ from the device tree and map to a VIRQ 52862306a36Sopenharmony_ci * @dev: The PCI device needing an IRQ 52962306a36Sopenharmony_ci * @slot: PCI slot number; passed when used as map_irq callback. Unused 53062306a36Sopenharmony_ci * @pin: PCI IRQ pin number; passed when used as map_irq callback. Unused 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * @slot and @pin are unused, but included in the function so that this 53362306a36Sopenharmony_ci * function can be used directly as the map_irq callback to 53462306a36Sopenharmony_ci * pci_assign_irq() and struct pci_host_bridge.map_irq pointer 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ciint of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct of_phandle_args oirq; 53962306a36Sopenharmony_ci int ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ret = of_irq_parse_pci(dev, &oirq); 54262306a36Sopenharmony_ci if (ret) 54362306a36Sopenharmony_ci return 0; /* Proper return code 0 == NO_IRQ */ 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return irq_create_of_mapping(&oirq); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); 54862306a36Sopenharmony_ci#endif /* CONFIG_OF_IRQ */ 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int pci_parse_request_of_pci_ranges(struct device *dev, 55162306a36Sopenharmony_ci struct pci_host_bridge *bridge) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci int err, res_valid = 0; 55462306a36Sopenharmony_ci resource_size_t iobase; 55562306a36Sopenharmony_ci struct resource_entry *win, *tmp; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge->windows); 55862306a36Sopenharmony_ci INIT_LIST_HEAD(&bridge->dma_ranges); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &bridge->windows, 56162306a36Sopenharmony_ci &bridge->dma_ranges, &iobase); 56262306a36Sopenharmony_ci if (err) 56362306a36Sopenharmony_ci return err; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci err = devm_request_pci_bus_resources(dev, &bridge->windows); 56662306a36Sopenharmony_ci if (err) 56762306a36Sopenharmony_ci return err; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { 57062306a36Sopenharmony_ci struct resource *res = win->res; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci switch (resource_type(res)) { 57362306a36Sopenharmony_ci case IORESOURCE_IO: 57462306a36Sopenharmony_ci err = devm_pci_remap_iospace(dev, res, iobase); 57562306a36Sopenharmony_ci if (err) { 57662306a36Sopenharmony_ci dev_warn(dev, "error %d: failed to map resource %pR\n", 57762306a36Sopenharmony_ci err, res); 57862306a36Sopenharmony_ci resource_list_destroy_entry(win); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci case IORESOURCE_MEM: 58262306a36Sopenharmony_ci res_valid |= !(res->flags & IORESOURCE_PREFETCH); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_PREFETCH)) 58562306a36Sopenharmony_ci if (upper_32_bits(resource_size(res))) 58662306a36Sopenharmony_ci dev_warn(dev, "Memory resource size exceeds max for 32 bits\n"); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (!res_valid) 59362306a36Sopenharmony_ci dev_warn(dev, "non-prefetchable memory resource required\n"); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ciint devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci if (!dev->of_node) 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci bridge->swizzle_irq = pci_common_swizzle; 60462306a36Sopenharmony_ci bridge->map_irq = of_irq_parse_and_map_pci; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return pci_parse_request_of_pci_ranges(dev, bridge); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci#ifdef CONFIG_PCI_DYNAMIC_OF_NODES 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_civoid of_pci_remove_node(struct pci_dev *pdev) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct device_node *np; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci np = pci_device_to_OF_node(pdev); 61662306a36Sopenharmony_ci if (!np || !of_node_check_flag(np, OF_DYNAMIC)) 61762306a36Sopenharmony_ci return; 61862306a36Sopenharmony_ci pdev->dev.of_node = NULL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci of_changeset_revert(np->data); 62162306a36Sopenharmony_ci of_changeset_destroy(np->data); 62262306a36Sopenharmony_ci of_node_put(np); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_civoid of_pci_make_dev_node(struct pci_dev *pdev) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct device_node *ppnode, *np = NULL; 62862306a36Sopenharmony_ci const char *pci_type; 62962306a36Sopenharmony_ci struct of_changeset *cset; 63062306a36Sopenharmony_ci const char *name; 63162306a36Sopenharmony_ci int ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* 63462306a36Sopenharmony_ci * If there is already a device tree node linked to this device, 63562306a36Sopenharmony_ci * return immediately. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ci if (pci_device_to_OF_node(pdev)) 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Check if there is device tree node for parent device */ 64162306a36Sopenharmony_ci if (!pdev->bus->self) 64262306a36Sopenharmony_ci ppnode = pdev->bus->dev.of_node; 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci ppnode = pdev->bus->self->dev.of_node; 64562306a36Sopenharmony_ci if (!ppnode) 64662306a36Sopenharmony_ci return; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (pci_is_bridge(pdev)) 64962306a36Sopenharmony_ci pci_type = "pci"; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci pci_type = "dev"; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s@%x,%x", pci_type, 65462306a36Sopenharmony_ci PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); 65562306a36Sopenharmony_ci if (!name) 65662306a36Sopenharmony_ci return; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci cset = kmalloc(sizeof(*cset), GFP_KERNEL); 65962306a36Sopenharmony_ci if (!cset) 66062306a36Sopenharmony_ci goto out_free_name; 66162306a36Sopenharmony_ci of_changeset_init(cset); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci np = of_changeset_create_node(cset, ppnode, name); 66462306a36Sopenharmony_ci if (!np) 66562306a36Sopenharmony_ci goto out_destroy_cset; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ret = of_pci_add_properties(pdev, cset, np); 66862306a36Sopenharmony_ci if (ret) 66962306a36Sopenharmony_ci goto out_free_node; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = of_changeset_apply(cset); 67262306a36Sopenharmony_ci if (ret) 67362306a36Sopenharmony_ci goto out_free_node; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci np->data = cset; 67662306a36Sopenharmony_ci pdev->dev.of_node = np; 67762306a36Sopenharmony_ci kfree(name); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciout_free_node: 68262306a36Sopenharmony_ci of_node_put(np); 68362306a36Sopenharmony_ciout_destroy_cset: 68462306a36Sopenharmony_ci of_changeset_destroy(cset); 68562306a36Sopenharmony_ci kfree(cset); 68662306a36Sopenharmony_ciout_free_name: 68762306a36Sopenharmony_ci kfree(name); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci#endif 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/** 69462306a36Sopenharmony_ci * of_pci_get_max_link_speed - Find the maximum link speed of the given device node. 69562306a36Sopenharmony_ci * @node: Device tree node with the maximum link speed information. 69662306a36Sopenharmony_ci * 69762306a36Sopenharmony_ci * This function will try to find the limitation of link speed by finding 69862306a36Sopenharmony_ci * a property called "max-link-speed" of the given device node. 69962306a36Sopenharmony_ci * 70062306a36Sopenharmony_ci * Return: 70162306a36Sopenharmony_ci * * > 0 - On success, a maximum link speed. 70262306a36Sopenharmony_ci * * -EINVAL - Invalid "max-link-speed" property value, or failure to access 70362306a36Sopenharmony_ci * the property of the device tree node. 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * Returns the associated max link speed from DT, or a negative value if the 70662306a36Sopenharmony_ci * required property is not found or is invalid. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ciint of_pci_get_max_link_speed(struct device_node *node) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci u32 max_link_speed; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (of_property_read_u32(node, "max-link-speed", &max_link_speed) || 71362306a36Sopenharmony_ci max_link_speed == 0 || max_link_speed > 4) 71462306a36Sopenharmony_ci return -EINVAL; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return max_link_speed; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_get_max_link_speed); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/** 72162306a36Sopenharmony_ci * of_pci_get_slot_power_limit - Parses the "slot-power-limit-milliwatt" 72262306a36Sopenharmony_ci * property. 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * @node: device tree node with the slot power limit information 72562306a36Sopenharmony_ci * @slot_power_limit_value: pointer where the value should be stored in PCIe 72662306a36Sopenharmony_ci * Slot Capabilities Register format 72762306a36Sopenharmony_ci * @slot_power_limit_scale: pointer where the scale should be stored in PCIe 72862306a36Sopenharmony_ci * Slot Capabilities Register format 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * Returns the slot power limit in milliwatts and if @slot_power_limit_value 73162306a36Sopenharmony_ci * and @slot_power_limit_scale pointers are non-NULL, fills in the value and 73262306a36Sopenharmony_ci * scale in format used by PCIe Slot Capabilities Register. 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * If the property is not found or is invalid, returns 0. 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ciu32 of_pci_get_slot_power_limit(struct device_node *node, 73762306a36Sopenharmony_ci u8 *slot_power_limit_value, 73862306a36Sopenharmony_ci u8 *slot_power_limit_scale) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci u32 slot_power_limit_mw; 74162306a36Sopenharmony_ci u8 value, scale; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (of_property_read_u32(node, "slot-power-limit-milliwatt", 74462306a36Sopenharmony_ci &slot_power_limit_mw)) 74562306a36Sopenharmony_ci slot_power_limit_mw = 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Calculate Slot Power Limit Value and Slot Power Limit Scale */ 74862306a36Sopenharmony_ci if (slot_power_limit_mw == 0) { 74962306a36Sopenharmony_ci value = 0x00; 75062306a36Sopenharmony_ci scale = 0; 75162306a36Sopenharmony_ci } else if (slot_power_limit_mw <= 255) { 75262306a36Sopenharmony_ci value = slot_power_limit_mw; 75362306a36Sopenharmony_ci scale = 3; 75462306a36Sopenharmony_ci } else if (slot_power_limit_mw <= 255*10) { 75562306a36Sopenharmony_ci value = slot_power_limit_mw / 10; 75662306a36Sopenharmony_ci scale = 2; 75762306a36Sopenharmony_ci slot_power_limit_mw = slot_power_limit_mw / 10 * 10; 75862306a36Sopenharmony_ci } else if (slot_power_limit_mw <= 255*100) { 75962306a36Sopenharmony_ci value = slot_power_limit_mw / 100; 76062306a36Sopenharmony_ci scale = 1; 76162306a36Sopenharmony_ci slot_power_limit_mw = slot_power_limit_mw / 100 * 100; 76262306a36Sopenharmony_ci } else if (slot_power_limit_mw <= 239*1000) { 76362306a36Sopenharmony_ci value = slot_power_limit_mw / 1000; 76462306a36Sopenharmony_ci scale = 0; 76562306a36Sopenharmony_ci slot_power_limit_mw = slot_power_limit_mw / 1000 * 1000; 76662306a36Sopenharmony_ci } else if (slot_power_limit_mw < 250*1000) { 76762306a36Sopenharmony_ci value = 0xEF; 76862306a36Sopenharmony_ci scale = 0; 76962306a36Sopenharmony_ci slot_power_limit_mw = 239*1000; 77062306a36Sopenharmony_ci } else if (slot_power_limit_mw <= 600*1000) { 77162306a36Sopenharmony_ci value = 0xF0 + (slot_power_limit_mw / 1000 - 250) / 25; 77262306a36Sopenharmony_ci scale = 0; 77362306a36Sopenharmony_ci slot_power_limit_mw = slot_power_limit_mw / (1000*25) * (1000*25); 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci value = 0xFE; 77662306a36Sopenharmony_ci scale = 0; 77762306a36Sopenharmony_ci slot_power_limit_mw = 600*1000; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (slot_power_limit_value) 78162306a36Sopenharmony_ci *slot_power_limit_value = value; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (slot_power_limit_scale) 78462306a36Sopenharmony_ci *slot_power_limit_scale = scale; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return slot_power_limit_mw; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit); 789