|
24 | 24 | #include <linux/pci.h>
|
25 | 25 | #include <linux/pci-ecam.h>
|
26 | 26 | #include <linux/printk.h>
|
| 27 | +#include <linux/regulator/consumer.h> |
27 | 28 | #include <linux/reset.h>
|
28 | 29 | #include <linux/sizes.h>
|
29 | 30 | #include <linux/slab.h>
|
@@ -260,6 +261,14 @@ static const struct pcie_cfg_data bcm2711_cfg = {
|
260 | 261 | .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
261 | 262 | };
|
262 | 263 |
|
| 264 | +struct subdev_regulators { |
| 265 | + unsigned int num_supplies; |
| 266 | + struct regulator_bulk_data supplies[]; |
| 267 | +}; |
| 268 | + |
| 269 | +static int pci_subdev_regulators_add_bus(struct pci_bus *bus); |
| 270 | +static void pci_subdev_regulators_remove_bus(struct pci_bus *bus); |
| 271 | + |
263 | 272 | struct brcm_msi {
|
264 | 273 | struct device *dev;
|
265 | 274 | void __iomem *base;
|
@@ -408,6 +417,71 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
|
408 | 417 | return ssc && pll ? 0 : -EIO;
|
409 | 418 | }
|
410 | 419 |
|
| 420 | +static void *alloc_subdev_regulators(struct device *dev) |
| 421 | +{ |
| 422 | + static const char * const supplies[] = { |
| 423 | + "vpcie3v3", |
| 424 | + "vpcie3v3aux", |
| 425 | + "vpcie12v", |
| 426 | + }; |
| 427 | + const size_t size = sizeof(struct subdev_regulators) |
| 428 | + + sizeof(struct regulator_bulk_data) * ARRAY_SIZE(supplies); |
| 429 | + struct subdev_regulators *sr; |
| 430 | + int i; |
| 431 | + |
| 432 | + sr = devm_kzalloc(dev, size, GFP_KERNEL); |
| 433 | + if (sr) { |
| 434 | + sr->num_supplies = ARRAY_SIZE(supplies); |
| 435 | + for (i = 0; i < ARRAY_SIZE(supplies); i++) |
| 436 | + sr->supplies[i].supply = supplies[i]; |
| 437 | + } |
| 438 | + |
| 439 | + return sr; |
| 440 | +} |
| 441 | + |
| 442 | +static int pci_subdev_regulators_add_bus(struct pci_bus *bus) |
| 443 | +{ |
| 444 | + struct device *dev = &bus->dev; |
| 445 | + struct subdev_regulators *sr; |
| 446 | + int ret; |
| 447 | + |
| 448 | + if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent)) |
| 449 | + return 0; |
| 450 | + |
| 451 | + if (dev->driver_data) |
| 452 | + dev_err(dev, "dev.driver_data unexpectedly non-NULL\n"); |
| 453 | + |
| 454 | + sr = alloc_subdev_regulators(dev); |
| 455 | + if (!sr) |
| 456 | + return -ENOMEM; |
| 457 | + |
| 458 | + dev->driver_data = sr; |
| 459 | + ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies); |
| 460 | + if (ret) |
| 461 | + return ret; |
| 462 | + |
| 463 | + ret = regulator_bulk_enable(sr->num_supplies, sr->supplies); |
| 464 | + if (ret) { |
| 465 | + dev_err(dev, "failed to enable regulators for downstream device\n"); |
| 466 | + return ret; |
| 467 | + } |
| 468 | + |
| 469 | + return 0; |
| 470 | +} |
| 471 | + |
| 472 | +static void pci_subdev_regulators_remove_bus(struct pci_bus *bus) |
| 473 | +{ |
| 474 | + struct device *dev = &bus->dev; |
| 475 | + struct subdev_regulators *sr = dev->driver_data; |
| 476 | + |
| 477 | + if (!sr || !bus->parent || !pci_is_root_bus(bus->parent)) |
| 478 | + return; |
| 479 | + |
| 480 | + if (regulator_bulk_disable(sr->num_supplies, sr->supplies)) |
| 481 | + dev_err(dev, "failed to disable regulators for downstream device\n"); |
| 482 | + dev->driver_data = NULL; |
| 483 | +} |
| 484 | + |
411 | 485 | /* Limits operation to a specific generation (1, 2, or 3) */
|
412 | 486 | static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
|
413 | 487 | {
|
@@ -731,6 +805,8 @@ static struct pci_ops brcm_pcie_ops = {
|
731 | 805 | .map_bus = brcm_pcie_map_conf,
|
732 | 806 | .read = pci_generic_config_read,
|
733 | 807 | .write = pci_generic_config_write,
|
| 808 | + .add_bus = pci_subdev_regulators_add_bus, |
| 809 | + .remove_bus = pci_subdev_regulators_remove_bus, |
734 | 810 | };
|
735 | 811 |
|
736 | 812 | static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
|
|
0 commit comments