Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Primary to Sideband (P2SB) bridge access support
0004  *
0005  * Copyright (c) 2017, 2021-2022 Intel Corporation.
0006  *
0007  * Authors: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
0008  *      Jonathan Yong <jonathan.yong@intel.com>
0009  */
0010 
0011 #include <linux/bits.h>
0012 #include <linux/export.h>
0013 #include <linux/pci.h>
0014 #include <linux/platform_data/x86/p2sb.h>
0015 
0016 #include <asm/cpu_device_id.h>
0017 #include <asm/intel-family.h>
0018 
0019 #define P2SBC           0xe0
0020 #define P2SBC_HIDE      BIT(8)
0021 
0022 static const struct x86_cpu_id p2sb_cpu_ids[] = {
0023     X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT,   PCI_DEVFN(13, 0)),
0024     X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, PCI_DEVFN(31, 1)),
0025     X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_D,   PCI_DEVFN(31, 1)),
0026     X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE,        PCI_DEVFN(31, 1)),
0027     X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L,      PCI_DEVFN(31, 1)),
0028     X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE,     PCI_DEVFN(31, 1)),
0029     X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L,       PCI_DEVFN(31, 1)),
0030     {}
0031 };
0032 
0033 static int p2sb_get_devfn(unsigned int *devfn)
0034 {
0035     const struct x86_cpu_id *id;
0036 
0037     id = x86_match_cpu(p2sb_cpu_ids);
0038     if (!id)
0039         return -ENODEV;
0040 
0041     *devfn = (unsigned int)id->driver_data;
0042     return 0;
0043 }
0044 
0045 /* Copy resource from the first BAR of the device in question */
0046 static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
0047 {
0048     struct resource *bar0 = &pdev->resource[0];
0049 
0050     /* Make sure we have no dangling pointers in the output */
0051     memset(mem, 0, sizeof(*mem));
0052 
0053     /*
0054      * We copy only selected fields from the original resource.
0055      * Because a PCI device will be removed soon, we may not use
0056      * any allocated data, hence we may not copy any pointers.
0057      */
0058     mem->start = bar0->start;
0059     mem->end = bar0->end;
0060     mem->flags = bar0->flags;
0061     mem->desc = bar0->desc;
0062 
0063     return 0;
0064 }
0065 
0066 static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
0067 {
0068     struct pci_dev *pdev;
0069     int ret;
0070 
0071     pdev = pci_scan_single_device(bus, devfn);
0072     if (!pdev)
0073         return -ENODEV;
0074 
0075     ret = p2sb_read_bar0(pdev, mem);
0076 
0077     pci_stop_and_remove_bus_device(pdev);
0078     return ret;
0079 }
0080 
0081 /**
0082  * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR
0083  * @bus: PCI bus to communicate with
0084  * @devfn: PCI slot and function to communicate with
0085  * @mem: memory resource to be filled in
0086  *
0087  * The BIOS prevents the P2SB device from being enumerated by the PCI
0088  * subsystem, so we need to unhide and hide it back to lookup the BAR.
0089  *
0090  * if @bus is NULL, the bus 0 in domain 0 will be used.
0091  * If @devfn is 0, it will be replaced by devfn of the P2SB device.
0092  *
0093  * Caller must provide a valid pointer to @mem.
0094  *
0095  * Locking is handled by pci_rescan_remove_lock mutex.
0096  *
0097  * Return:
0098  * 0 on success or appropriate errno value on error.
0099  */
0100 int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
0101 {
0102     struct pci_dev *pdev_p2sb;
0103     unsigned int devfn_p2sb;
0104     u32 value = P2SBC_HIDE;
0105     int ret;
0106 
0107     /* Get devfn for P2SB device itself */
0108     ret = p2sb_get_devfn(&devfn_p2sb);
0109     if (ret)
0110         return ret;
0111 
0112     /* if @bus is NULL, use bus 0 in domain 0 */
0113     bus = bus ?: pci_find_bus(0, 0);
0114 
0115     /*
0116      * Prevent concurrent PCI bus scan from seeing the P2SB device and
0117      * removing via sysfs while it is temporarily exposed.
0118      */
0119     pci_lock_rescan_remove();
0120 
0121     /* Unhide the P2SB device, if needed */
0122     pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value);
0123     if (value & P2SBC_HIDE)
0124         pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0);
0125 
0126     pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb);
0127     if (devfn)
0128         ret = p2sb_scan_and_read(bus, devfn, mem);
0129     else
0130         ret = p2sb_read_bar0(pdev_p2sb, mem);
0131     pci_stop_and_remove_bus_device(pdev_p2sb);
0132 
0133     /* Hide the P2SB device, if it was hidden */
0134     if (value & P2SBC_HIDE)
0135         pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, P2SBC_HIDE);
0136 
0137     pci_unlock_rescan_remove();
0138 
0139     if (ret)
0140         return ret;
0141 
0142     if (mem->flags == 0)
0143         return -ENODEV;
0144 
0145     return 0;
0146 }
0147 EXPORT_SYMBOL_GPL(p2sb_bar);