Pull x86 platform driver fixes from Ilpo Järvinen: - Intel PMC GBE LTR regression - P2SB / PCI deadlock fix * tag 'platform-drivers-x86-v6.7-6' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: platform/x86/intel/pmc: Move GBE LTR ignore to suspend callback platform/x86/intel/pmc: Allow reenabling LTRs platform/x86/intel/pmc: Add suspend callback platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe
Unified · Split
| @@ -314,16 +314,13 @@ int adl_core_init(struct pmc_dev *pmcdev) · 313 unmodified lines | |||
| 314 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; | 314 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 315 | int ret; | 315 | int ret; |
| 316 | 316 | ||
| 317 | pmcdev->suspend = cnl_suspend; | ||
| 318 | pmcdev->resume = cnl_resume; | ||
| 319 | |||
| 317 | pmc->map = &adl_reg_map; | 320 | pmc->map = &adl_reg_map; |
| 318 | ret = get_primary_reg_base(pmc); | 321 | ret = get_primary_reg_base(pmc); |
| 319 | if (ret) | 322 | if (ret) |
| 320 | return ret; | 323 | return ret; |
| 321 | 324 | ||
| 322 | /* Due to a hardware limitation, the GBE LTR blocks PC10 | ||
| 323 | * when a cable is attached. Tell the PMC to ignore it. | ||
| 324 | */ | ||
| 325 | dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | ||
| 326 | pmc_core_send_ltr_ignore(pmcdev, 3); | ||
| 327 | |||
| 328 | return 0; | 325 | return 0; |
| 329 | } | 326 | } |
| @@ -204,21 +204,35 @@ const struct pmc_reg_map cnp_reg_map = { · 203 unmodified lines | |||
| 204 | .etr3_offset = ETR3_OFFSET, | 204 | .etr3_offset = ETR3_OFFSET, |
| 205 | }; | 205 | }; |
| 206 | 206 | ||
| 207 | void cnl_suspend(struct pmc_dev *pmcdev) | ||
| 208 | { | ||
| 209 | /* | ||
| 210 | * Due to a hardware limitation, the GBE LTR blocks PC10 | ||
| 211 | * when a cable is attached. To unblock PC10 during suspend, | ||
| 212 | * tell the PMC to ignore it. | ||
| 213 | */ | ||
| 214 | pmc_core_send_ltr_ignore(pmcdev, 3, 1); | ||
| 215 | } | ||
| 216 | |||
| 217 | int cnl_resume(struct pmc_dev *pmcdev) | ||
| 218 | { | ||
| 219 | pmc_core_send_ltr_ignore(pmcdev, 3, 0); | ||
| 220 | |||
| 221 | return pmc_core_resume_common(pmcdev); | ||
| 222 | } | ||
| 223 | |||
| 207 | int cnp_core_init(struct pmc_dev *pmcdev) | 224 | int cnp_core_init(struct pmc_dev *pmcdev) |
| 208 | { | 225 | { |
| 209 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; | 226 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 210 | int ret; | 227 | int ret; |
| 211 | 228 | ||
| 229 | pmcdev->suspend = cnl_suspend; | ||
| 230 | pmcdev->resume = cnl_resume; | ||
| 231 | |||
| 212 | pmc->map = &cnp_reg_map; | 232 | pmc->map = &cnp_reg_map; |
| 213 | ret = get_primary_reg_base(pmc); | 233 | ret = get_primary_reg_base(pmc); |
| 214 | if (ret) | 234 | if (ret) |
| 215 | return ret; | 235 | return ret; |
| 216 | 236 | ||
| 217 | /* Due to a hardware limitation, the GBE LTR blocks PC10 | ||
| 218 | * when a cable is attached. Tell the PMC to ignore it. | ||
| 219 | */ | ||
| 220 | dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | ||
| 221 | pmc_core_send_ltr_ignore(pmcdev, 3); | ||
| 222 | |||
| 223 | return 0; | 237 | return 0; |
| 224 | } | 238 | } |
| @@ -460,7 +460,7 @@ out_unlock: · 459 unmodified lines | |||
| 460 | } | 460 | } |
| 461 | DEFINE_SHOW_ATTRIBUTE(pmc_core_pll); | 461 | DEFINE_SHOW_ATTRIBUTE(pmc_core_pll); |
| 462 | 462 | ||
| 463 | int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) | 463 | int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) |
| 464 | { | 464 | { |
| 465 | struct pmc *pmc; | 465 | struct pmc *pmc; |
| 466 | const struct pmc_reg_map *map; | 466 | const struct pmc_reg_map *map; |
| @@ -498,7 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) · 31 unmodified lines | |||
| 498 | mutex_lock(&pmcdev->lock); | 498 | mutex_lock(&pmcdev->lock); |
| 499 | 499 | ||
| 500 | reg = pmc_core_reg_read(pmc, map->ltr_ignore_offset); | 500 | reg = pmc_core_reg_read(pmc, map->ltr_ignore_offset); |
| 501 | reg |= BIT(ltr_index); | 501 | if (ignore) |
| 502 | reg |= BIT(ltr_index); | ||
| 503 | else | ||
| 504 | reg &= ~BIT(ltr_index); | ||
| 502 | pmc_core_reg_write(pmc, map->ltr_ignore_offset, reg); | 505 | pmc_core_reg_write(pmc, map->ltr_ignore_offset, reg); |
| 503 | 506 | ||
| 504 | mutex_unlock(&pmcdev->lock); | 507 | mutex_unlock(&pmcdev->lock); |
| @@ -521,7 +524,7 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file, · 16 unmodified lines | |||
| 521 | if (err) | 524 | if (err) |
| 522 | return err; | 525 | return err; |
| 523 | 526 | ||
| 524 | err = pmc_core_send_ltr_ignore(pmcdev, value); | 527 | err = pmc_core_send_ltr_ignore(pmcdev, value, 1); |
| 525 | 528 | ||
| 526 | return err == 0 ? count : err; | 529 | return err == 0 ? count : err; |
| 527 | } | 530 | } |
| @@ -1279,6 +1282,9 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) · 751 unmodified lines | |||
| 1279 | struct pmc_dev *pmcdev = dev_get_drvdata(dev); | 1282 | struct pmc_dev *pmcdev = dev_get_drvdata(dev); |
| 1280 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; | 1283 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 1281 | 1284 | ||
| 1285 | if (pmcdev->suspend) | ||
| 1286 | pmcdev->suspend(pmcdev); | ||
| 1287 | |||
| 1282 | /* Check if the syspend will actually use S0ix */ | 1288 | /* Check if the syspend will actually use S0ix */ |
| 1283 | if (pm_suspend_via_firmware()) | 1289 | if (pm_suspend_via_firmware()) |
| 1284 | return 0; | 1290 | return 0; |
| @@ -363,6 +363,7 @@ struct pmc { · 362 unmodified lines | |||
| 363 | * @s0ix_counter: S0ix residency (step adjusted) | 363 | * @s0ix_counter: S0ix residency (step adjusted) |
| 364 | * @num_lpm_modes: Count of enabled modes | 364 | * @num_lpm_modes: Count of enabled modes |
| 365 | * @lpm_en_modes: Array of enabled modes from lowest to highest priority | 365 | * @lpm_en_modes: Array of enabled modes from lowest to highest priority |
| 366 | * @suspend: Function to perform platform specific suspend | ||
| 366 | * @resume: Function to perform platform specific resume | 367 | * @resume: Function to perform platform specific resume |
| 367 | * | 368 | * |
| 368 | * pmc_dev contains info about power management controller device. | 369 | * pmc_dev contains info about power management controller device. |
| @@ -379,6 +380,7 @@ struct pmc_dev { · 10 unmodified lines | |||
| 379 | u64 s0ix_counter; | 380 | u64 s0ix_counter; |
| 380 | int num_lpm_modes; | 381 | int num_lpm_modes; |
| 381 | int lpm_en_modes[LPM_MAX_NUM_MODES]; | 382 | int lpm_en_modes[LPM_MAX_NUM_MODES]; |
| 383 | void (*suspend)(struct pmc_dev *pmcdev); | ||
| 382 | int (*resume)(struct pmc_dev *pmcdev); | 384 | int (*resume)(struct pmc_dev *pmcdev); |
| 383 | 385 | ||
| 384 | bool has_die_c6; | 386 | bool has_die_c6; |
| @@ -486,7 +488,7 @@ extern const struct pmc_bit_map *mtl_ioem_lpm_maps[]; · 101 unmodified lines | |||
| 486 | extern const struct pmc_reg_map mtl_ioem_reg_map; | 488 | extern const struct pmc_reg_map mtl_ioem_reg_map; |
| 487 | 489 | ||
| 488 | extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); | 490 | extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); |
| 489 | extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); | 491 | int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); |
| 490 | 492 | ||
| 491 | int pmc_core_resume_common(struct pmc_dev *pmcdev); | 493 | int pmc_core_resume_common(struct pmc_dev *pmcdev); |
| 492 | int get_primary_reg_base(struct pmc *pmc); | 494 | int get_primary_reg_base(struct pmc *pmc); |
| @@ -500,6 +502,9 @@ int tgl_core_init(struct pmc_dev *pmcdev); · 7 unmodified lines | |||
| 500 | int adl_core_init(struct pmc_dev *pmcdev); | 502 | int adl_core_init(struct pmc_dev *pmcdev); |
| 501 | int mtl_core_init(struct pmc_dev *pmcdev); | 503 | int mtl_core_init(struct pmc_dev *pmcdev); |
| 502 | 504 | ||
| 505 | void cnl_suspend(struct pmc_dev *pmcdev); | ||
| 506 | int cnl_resume(struct pmc_dev *pmcdev); | ||
| 507 | |||
| 503 | #define pmc_for_each_mode(i, mode, pmcdev) \ | 508 | #define pmc_for_each_mode(i, mode, pmcdev) \ |
| 504 | for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ | 509 | for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ |
| 505 | i < pmcdev->num_lpm_modes; \ | 510 | i < pmcdev->num_lpm_modes; \ |
| @@ -979,6 +979,8 @@ static void mtl_d3_fixup(void) · 978 unmodified lines | |||
| 979 | static int mtl_resume(struct pmc_dev *pmcdev) | 979 | static int mtl_resume(struct pmc_dev *pmcdev) |
| 980 | { | 980 | { |
| 981 | mtl_d3_fixup(); | 981 | mtl_d3_fixup(); |
| 982 | pmc_core_send_ltr_ignore(pmcdev, 3, 0); | ||
| 983 | |||
| 982 | return pmc_core_resume_common(pmcdev); | 984 | return pmc_core_resume_common(pmcdev); |
| 983 | } | 985 | } |
| 984 | 986 | ||
| @@ -989,6 +991,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) · 4 unmodified lines | |||
| 989 | 991 | ||
| 990 | mtl_d3_fixup(); | 992 | mtl_d3_fixup(); |
| 991 | 993 | ||
| 994 | pmcdev->suspend = cnl_suspend; | ||
| 992 | pmcdev->resume = mtl_resume; | 995 | pmcdev->resume = mtl_resume; |
| 993 | 996 | ||
| 994 | pmcdev->regmap_list = mtl_pmc_info_list; | 997 | pmcdev->regmap_list = mtl_pmc_info_list; |
| @@ -1002,11 +1005,5 @@ int mtl_core_init(struct pmc_dev *pmcdev) · 7 unmodified lines | |||
| 1002 | return ret; | 1005 | return ret; |
| 1003 | } | 1006 | } |
| 1004 | 1007 | ||
| 1005 | /* Due to a hardware limitation, the GBE LTR blocks PC10 | ||
| 1006 | * when a cable is attached. Tell the PMC to ignore it. | ||
| 1007 | */ | ||
| 1008 | dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | ||
| 1009 | pmc_core_send_ltr_ignore(pmcdev, 3); | ||
| 1010 | |||
| 1011 | return 0; | 1008 | return 0; |
| 1012 | } | 1009 | } |
| @@ -259,16 +259,15 @@ int tgl_core_init(struct pmc_dev *pmcdev) · 258 unmodified lines | |||
| 259 | int ret; | 259 | int ret; |
| 260 | 260 | ||
| 261 | pmc->map = &tgl_reg_map; | 261 | pmc->map = &tgl_reg_map; |
| 262 | |||
| 263 | pmcdev->suspend = cnl_suspend; | ||
| 264 | pmcdev->resume = cnl_resume; | ||
| 265 | |||
| 262 | ret = get_primary_reg_base(pmc); | 266 | ret = get_primary_reg_base(pmc); |
| 263 | if (ret) | 267 | if (ret) |
| 264 | return ret; | 268 | return ret; |
| 265 | 269 | ||
| 266 | pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); | 270 | pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); |
| 267 | /* Due to a hardware limitation, the GBE LTR blocks PC10 | ||
| 268 | * when a cable is attached. Tell the PMC to ignore it. | ||
| 269 | */ | ||
| 270 | dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | ||
| 271 | pmc_core_send_ltr_ignore(pmcdev, 3); | ||
| 272 | 271 | ||
| 273 | return 0; | 272 | return 0; |
| 274 | } | 273 | } |
| @@ -26,6 +26,21 @@ static const struct x86_cpu_id p2sb_cpu_ids[] = { · 25 unmodified lines | |||
| 26 | {} | 26 | {} |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | /* | ||
| 30 | * Cache BAR0 of P2SB device functions 0 to 7. | ||
| 31 | * TODO: The constant 8 is the number of functions that PCI specification | ||
| 32 | * defines. Same definitions exist tree-wide. Unify this definition and | ||
| 33 | * the other definitions then move to include/uapi/linux/pci.h. | ||
| 34 | */ | ||
| 35 | #define NR_P2SB_RES_CACHE 8 | ||
| 36 | |||
| 37 | struct p2sb_res_cache { | ||
| 38 | u32 bus_dev_id; | ||
| 39 | struct resource res; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static struct p2sb_res_cache p2sb_resources[NR_P2SB_RES_CACHE]; | ||
| 43 | |||
| 29 | static int p2sb_get_devfn(unsigned int *devfn) | 44 | static int p2sb_get_devfn(unsigned int *devfn) |
| 30 | { | 45 | { |
| 31 | unsigned int fn = P2SB_DEVFN_DEFAULT; | 46 | unsigned int fn = P2SB_DEVFN_DEFAULT; |
| @@ -39,8 +54,16 @@ static int p2sb_get_devfn(unsigned int *devfn) · 7 unmodified lines | |||
| 39 | return 0; | 54 | return 0; |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 57 | static bool p2sb_valid_resource(struct resource *res) | ||
| 58 | { | ||
| 59 | if (res->flags) | ||
| 60 | return true; | ||
| 61 | |||
| 62 | return false; | ||
| 63 | } | ||
| 64 | |||
| 42 | /* Copy resource from the first BAR of the device in question */ | 65 | /* Copy resource from the first BAR of the device in question */ |
| 43 | static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) | 66 | static void p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) |
| 44 | { | 67 | { |
| 45 | struct resource *bar0 = &pdev->resource[0]; | 68 | struct resource *bar0 = &pdev->resource[0]; |
| 46 | 69 | ||
| @@ -56,47 +79,64 @@ static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) · 9 unmodified lines | |||
| 56 | mem->end = bar0->end; | 79 | mem->end = bar0->end; |
| 57 | mem->flags = bar0->flags; | 80 | mem->flags = bar0->flags; |
| 58 | mem->desc = bar0->desc; | 81 | mem->desc = bar0->desc; |
| 59 | |||
| 60 | return 0; | ||
| 61 | } | 82 | } |
| 62 | 83 | ||
| 63 | static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | 84 | static void p2sb_scan_and_cache_devfn(struct pci_bus *bus, unsigned int devfn) |
| 64 | { | 85 | { |
| 86 | struct p2sb_res_cache *cache = &p2sb_resources[PCI_FUNC(devfn)]; | ||
| 65 | struct pci_dev *pdev; | 87 | struct pci_dev *pdev; |
| 66 | int ret; | ||
| 67 | 88 | ||
| 68 | pdev = pci_scan_single_device(bus, devfn); | 89 | pdev = pci_scan_single_device(bus, devfn); |
| 69 | if (!pdev) | 90 | if (!pdev) |
| 70 | return -ENODEV; | 91 | return; |
| 71 | 92 | ||
| 72 | ret = p2sb_read_bar0(pdev, mem); | 93 | p2sb_read_bar0(pdev, &cache->res); |
| 94 | cache->bus_dev_id = bus->dev.id; | ||
| 73 | 95 | ||
| 74 | pci_stop_and_remove_bus_device(pdev); | 96 | pci_stop_and_remove_bus_device(pdev); |
| 75 | return ret; | 97 | return; |
| 76 | } | 98 | } |
| 77 | 99 | ||
| 78 | /** | 100 | static int p2sb_scan_and_cache(struct pci_bus *bus, unsigned int devfn) |
| 79 | * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR | 101 | { |
| 80 | * @bus: PCI bus to communicate with | 102 | unsigned int slot, fn; |
| 81 | * @devfn: PCI slot and function to communicate with | 103 | |
| 82 | * @mem: memory resource to be filled in | 104 | if (PCI_FUNC(devfn) == 0) { |
| 83 | * | 105 | /* |
| 84 | * The BIOS prevents the P2SB device from being enumerated by the PCI | 106 | * When function number of the P2SB device is zero, scan it and |
| 85 | * subsystem, so we need to unhide and hide it back to lookup the BAR. | 107 | * other function numbers, and if devices are available, cache |
| 86 | * | 108 | * their BAR0s. |
| 87 | * if @bus is NULL, the bus 0 in domain 0 will be used. | 109 | */ |
| 88 | * If @devfn is 0, it will be replaced by devfn of the P2SB device. | 110 | slot = PCI_SLOT(devfn); |
| 89 | * | 111 | for (fn = 0; fn < NR_P2SB_RES_CACHE; fn++) |
| 90 | * Caller must provide a valid pointer to @mem. | 112 | p2sb_scan_and_cache_devfn(bus, PCI_DEVFN(slot, fn)); |
| 91 | * | 113 | } else { |
| 92 | * Locking is handled by pci_rescan_remove_lock mutex. | 114 | /* Scan the P2SB device and cache its BAR0 */ |
| 93 | * | 115 | p2sb_scan_and_cache_devfn(bus, devfn); |
| 94 | * Return: | 116 | } |
| 95 | * 0 on success or appropriate errno value on error. | 117 | |
| 96 | */ | 118 | if (!p2sb_valid_resource(&p2sb_resources[PCI_FUNC(devfn)].res)) |
| 97 | int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | 119 | return -ENOENT; |
| 120 | |||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | static struct pci_bus *p2sb_get_bus(struct pci_bus *bus) | ||
| 125 | { | ||
| 126 | static struct pci_bus *p2sb_bus; | ||
| 127 | |||
| 128 | bus = bus ?: p2sb_bus; | ||
| 129 | if (bus) | ||
| 130 | return bus; | ||
| 131 | |||
| 132 | /* Assume P2SB is on the bus 0 in domain 0 */ | ||
| 133 | p2sb_bus = pci_find_bus(0, 0); | ||
| 134 | return p2sb_bus; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int p2sb_cache_resources(void) | ||
| 98 | { | 138 | { |
| 99 | struct pci_dev *pdev_p2sb; | 139 | struct pci_bus *bus; |
| 100 | unsigned int devfn_p2sb; | 140 | unsigned int devfn_p2sb; |
| 101 | u32 value = P2SBC_HIDE; | 141 | u32 value = P2SBC_HIDE; |
| 102 | int ret; | 142 | int ret; |
| @@ -106,8 +146,9 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 3 unmodified lines | |||
| 106 | if (ret) | 146 | if (ret) |
| 107 | return ret; | 147 | return ret; |
| 108 | 148 | ||
| 109 | /* if @bus is NULL, use bus 0 in domain 0 */ | 149 | bus = p2sb_get_bus(NULL); |
| 110 | bus = bus ?: pci_find_bus(0, 0); | 150 | if (!bus) |
| 151 | return -ENODEV; | ||
| 111 | 152 | ||
| 112 | /* | 153 | /* |
| 113 | * Prevent concurrent PCI bus scan from seeing the P2SB device and | 154 | * Prevent concurrent PCI bus scan from seeing the P2SB device and |
| @@ -115,17 +156,16 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 1 unmodified line | |||
| 115 | */ | 156 | */ |
| 116 | pci_lock_rescan_remove(); | 157 | pci_lock_rescan_remove(); |
| 117 | 158 | ||
| 118 | /* Unhide the P2SB device, if needed */ | 159 | /* |
| 160 | * The BIOS prevents the P2SB device from being enumerated by the PCI | ||
| 161 | * subsystem, so we need to unhide and hide it back to lookup the BAR. | ||
| 162 | * Unhide the P2SB device here, if needed. | ||
| 163 | */ | ||
| 119 | pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value); | 164 | pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value); |
| 120 | if (value & P2SBC_HIDE) | 165 | if (value & P2SBC_HIDE) |
| 121 | pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0); | 166 | pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0); |
| 122 | 167 | ||
| 123 | pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb); | 168 | ret = p2sb_scan_and_cache(bus, devfn_p2sb); |
| 124 | if (devfn) | ||
| 125 | ret = p2sb_scan_and_read(bus, devfn, mem); | ||
| 126 | else | ||
| 127 | ret = p2sb_read_bar0(pdev_p2sb, mem); | ||
| 128 | pci_stop_and_remove_bus_device(pdev_p2sb); | ||
| 129 | 169 | ||
| 130 | /* Hide the P2SB device, if it was hidden */ | 170 | /* Hide the P2SB device, if it was hidden */ |
| 131 | if (value & P2SBC_HIDE) | 171 | if (value & P2SBC_HIDE) |
| @@ -133,12 +173,62 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 1 unmodified line | |||
| 133 | 173 | ||
| 134 | pci_unlock_rescan_remove(); | 174 | pci_unlock_rescan_remove(); |
| 135 | 175 | ||
| 136 | if (ret) | 176 | return ret; |
| 137 | return ret; | 177 | } |
| 178 | |||
| 179 | /** | ||
| 180 | * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR | ||
| 181 | * @bus: PCI bus to communicate with | ||
| 182 | * @devfn: PCI slot and function to communicate with | ||
| 183 | * @mem: memory resource to be filled in | ||
| 184 | * | ||
| 185 | * If @bus is NULL, the bus 0 in domain 0 will be used. | ||
| 186 | * If @devfn is 0, it will be replaced by devfn of the P2SB device. | ||
| 187 | * | ||
| 188 | * Caller must provide a valid pointer to @mem. | ||
| 189 | * | ||
| 190 | * Return: | ||
| 191 | * 0 on success or appropriate errno value on error. | ||
| 192 | */ | ||
| 193 | int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | ||
| 194 | { | ||
| 195 | struct p2sb_res_cache *cache; | ||
| 196 | int ret; | ||
| 138 | 197 | ||
| 139 | if (mem->flags == 0) | 198 | bus = p2sb_get_bus(bus); |
| 199 | if (!bus) | ||
| 140 | return -ENODEV; | 200 | return -ENODEV; |
| 141 | 201 | ||
| 202 | if (!devfn) { | ||
| 203 | ret = p2sb_get_devfn(&devfn); | ||
| 204 | if (ret) | ||
| 205 | return ret; | ||
| 206 | } | ||
| 207 | |||
| 208 | cache = &p2sb_resources[PCI_FUNC(devfn)]; | ||
| 209 | if (cache->bus_dev_id != bus->dev.id) | ||
| 210 | return -ENODEV; | ||
| 211 | |||
| 212 | if (!p2sb_valid_resource(&cache->res)) | ||
| 213 | return -ENOENT; | ||
| 214 | |||
| 215 | memcpy(mem, &cache->res, sizeof(*mem)); | ||
| 142 | return 0; | 216 | return 0; |
| 143 | } | 217 | } |
| 144 | EXPORT_SYMBOL_GPL(p2sb_bar); | 218 | EXPORT_SYMBOL_GPL(p2sb_bar); |
| 219 | |||
| 220 | static int __init p2sb_fs_init(void) | ||
| 221 | { | ||
| 222 | p2sb_cache_resources(); | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* | ||
| 227 | * pci_rescan_remove_lock to avoid access to unhidden P2SB devices can | ||
| 228 | * not be locked in sysfs pci bus rescan path because of deadlock. To | ||
| 229 | * avoid the deadlock, access to P2SB devices with the lock at an early | ||
| 230 | * step in kernel initialization and cache required resources. This | ||
| 231 | * should happen after subsys_initcall which initializes PCI subsystem | ||
| 232 | * and before device_initcall which requires P2SB resources. | ||
| 233 | */ | ||
| 234 | fs_initcall(p2sb_fs_init); | ||