From 18b9d43fe3c5384d61939b68d88a2da091e6674e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jan 2026 12:58:53 +0530 Subject: [PATCH 01/12] FROMLIST: PCI/pwrctrl: pwrseq: Rename private struct and pointers for consistency Previously the pwrseq, tc9563, and slot pwrctrl drivers used different naming conventions for their private data structs and pointers to them, which makes patches hard to read: Previous names New names ------------------------------------ ---------------------------------- struct pci_pwrctrl_pwrseq_data { struct pci_pwrctrl_pwrseq { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_pwrseq_data *data struct pci_pwrctrl_pwrseq *pwrseq struct tc9563_pwrctrl_ctx { struct pci_pwrctrl_tc9563 { struct tc9563_pwrctrl_ctx *ctx struct pci_pwrctrl_tc9563 *tc9563 struct pci_pwrctrl_slot_data { struct pci_pwrctrl_slot { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_slot_data *slot struct pci_pwrctrl_slot *slot Rename "struct pci_pwrctrl_pwrseq_data" to "pci_pwrctrl_pwrseq". Rename the "struct pci_pwrctrl ctx" member to "struct pci_pwrctrl pwrctrl". Rename pointers from "struct pci_pwrctrl_pwrseq_data *data" to "struct pci_pwrctrl_pwrseq *pwrseq". No functional change intended. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-1-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bartosz Golaszewski --- drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index 4e664e7b8dd23..c0d22dc3a8561 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -13,8 +13,8 @@ #include #include -struct pci_pwrctrl_pwrseq_data { - struct pci_pwrctrl ctx; +struct pci_pwrctrl_pwrseq { + struct pci_pwrctrl pwrctrl; struct pwrseq_desc *pwrseq; }; @@ -62,7 +62,7 @@ static void devm_pci_pwrctrl_pwrseq_power_off(void *data) static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) { const struct pci_pwrctrl_pwrseq_pdata *pdata; - struct pci_pwrctrl_pwrseq_data *data; + struct pci_pwrctrl_pwrseq *pwrseq; struct device *dev = &pdev->dev; int ret; @@ -76,28 +76,28 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) return ret; } - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); + if (!pwrseq) return -ENOMEM; - data->pwrseq = devm_pwrseq_get(dev, pdata->target); - if (IS_ERR(data->pwrseq)) - return dev_err_probe(dev, PTR_ERR(data->pwrseq), + pwrseq->pwrseq = devm_pwrseq_get(dev, pdata->target); + if (IS_ERR(pwrseq->pwrseq)) + return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq), "Failed to get the power sequencer\n"); - ret = pwrseq_power_on(data->pwrseq); + ret = pwrseq_power_on(pwrseq->pwrseq); if (ret) return dev_err_probe(dev, ret, "Failed to power-on the device\n"); ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off, - data->pwrseq); + pwrseq->pwrseq); if (ret) return ret; - pci_pwrctrl_init(&data->ctx, dev); + pci_pwrctrl_init(&pwrseq->pwrctrl, dev); - ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx); + ret = devm_pci_pwrctrl_device_set_ready(dev, &pwrseq->pwrctrl); if (ret) return dev_err_probe(dev, ret, "Failed to register the pwrctrl wrapper\n"); From 017091723003704e4fa4a66da273f909d8763224 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jan 2026 12:58:54 +0530 Subject: [PATCH 02/12] FROMLIST: PCI/pwrctrl: slot: Rename private struct and pointers for consistency Previously the pwrseq, tc9563, and slot pwrctrl drivers used different naming conventions for their private data structs and pointers to them, which makes patches hard to read: Previous names New names ------------------------------------ ---------------------------------- struct pci_pwrctrl_pwrseq_data { struct pci_pwrctrl_pwrseq { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_pwrseq_data *data struct pci_pwrctrl_pwrseq *pwrseq struct tc9563_pwrctrl_ctx { struct pci_pwrctrl_tc9563 { struct tc9563_pwrctrl_ctx *ctx struct pci_pwrctrl_tc9563 *tc9563 struct pci_pwrctrl_slot_data { struct pci_pwrctrl_slot { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_slot_data *slot struct pci_pwrctrl_slot *slot Rename "struct pci_pwrctrl_slot_data" to "struct pci_pwrctrl_slot". Rename the "struct pci_pwrctrl ctx" member to "struct pci_pwrctrl pwrctrl". No functional change intended. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-2-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bartosz Golaszewski --- drivers/pci/pwrctrl/slot.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 3320494b62d89..5ddae4ae34315 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -13,15 +13,15 @@ #include #include -struct pci_pwrctrl_slot_data { - struct pci_pwrctrl ctx; +struct pci_pwrctrl_slot { + struct pci_pwrctrl pwrctrl; struct regulator_bulk_data *supplies; int num_supplies; }; static void devm_pci_pwrctrl_slot_power_off(void *data) { - struct pci_pwrctrl_slot_data *slot = data; + struct pci_pwrctrl_slot *slot = data; regulator_bulk_disable(slot->num_supplies, slot->supplies); regulator_bulk_free(slot->num_supplies, slot->supplies); @@ -29,7 +29,7 @@ static void devm_pci_pwrctrl_slot_power_off(void *data) static int pci_pwrctrl_slot_probe(struct platform_device *pdev) { - struct pci_pwrctrl_slot_data *slot; + struct pci_pwrctrl_slot *slot; struct device *dev = &pdev->dev; struct clk *clk; int ret; @@ -64,9 +64,9 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) "Failed to enable slot clock\n"); } - pci_pwrctrl_init(&slot->ctx, dev); + pci_pwrctrl_init(&slot->pwrctrl, dev); - ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx); + ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl); if (ret) return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); From f526eba1c5ac4a5d8c2e67e19d7b881b0f5441da Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:58:55 +0530 Subject: [PATCH 03/12] FROMLIST: PCI/pwrctrl: tc9563: Use put_device() instead of i2c_put_adapter() The API comment for of_find_i2c_adapter_by_node() recommends using put_device() to drop the reference count of I2C adapter instead of using i2c_put_adapter(). So replace i2c_put_adapter() with put_device(). Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-3-9d26da3ce903@oss.qualcomm.com/ Fixes: 4c9c7be47310 ("PCI: pwrctrl: Add power control driver for TC9563") Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam Reviewed-by: Bartosz Golaszewski --- drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index 63e719fa3589a..28a2e4eaa7603 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -527,7 +527,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) ctx->client = i2c_new_dummy_device(ctx->adapter, addr); if (IS_ERR(ctx->client)) { dev_err(dev, "Failed to create I2C client\n"); - i2c_put_adapter(ctx->adapter); + put_device(&ctx->adapter->dev); return PTR_ERR(ctx->client); } @@ -602,7 +602,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) tc9563_pwrctrl_power_off(ctx); remove_i2c: i2c_unregister_device(ctx->client); - i2c_put_adapter(ctx->adapter); + put_device(&ctx->adapter->dev); return ret; } @@ -612,7 +612,7 @@ static void tc9563_pwrctrl_remove(struct platform_device *pdev) tc9563_pwrctrl_power_off(ctx); i2c_unregister_device(ctx->client); - i2c_put_adapter(ctx->adapter); + put_device(&ctx->adapter->dev); } static const struct of_device_id tc9563_pwrctrl_of_match[] = { From 15012487520e04799665c7c243f85611f5376f8f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jan 2026 12:58:56 +0530 Subject: [PATCH 04/12] FROMLIST: PCI/pwrctrl: tc9563: Clean up whitespace Most of pci-pwrctrl-tc9563.c fits in 80 columns. Wrap lines that are gratuitously longer. Whitespace changes only. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-4-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam --- drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 65 ++++++++++++++++-------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index 28a2e4eaa7603..b51947fa9300c 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -57,6 +57,9 @@ #define TC9563_POWER_CONTROL_OVREN 0x82b2c8 #define TC9563_GPIO_MASK 0xfffffff3 +#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */ + +#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */ #define TC9563_TX_MARGIN_MIN_VAL 400000 @@ -206,7 +209,8 @@ static int tc9563_pwrctrl_i2c_read(struct i2c_client *client, } static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client, - const struct tc9563_pwrctrl_reg_setting *seq, int len) + const struct tc9563_pwrctrl_reg_setting *seq, + int len) { int ret, i; @@ -241,12 +245,13 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx, if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, - common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq)); + return tc9563_pwrctrl_i2c_bulk_write(ctx->client, common_pwroff_seq, + ARRAY_SIZE(common_pwroff_seq)); } static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, - enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns) + enum tc9563_pwrctrl_ports port, + bool is_l1, u32 ns) { u32 rd_val, units; int ret; @@ -258,24 +263,32 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, units = ns / 256; if (port == TC9563_ETHERNET) { - ret = tc9563_pwrctrl_i2c_read(ctx->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val); + ret = tc9563_pwrctrl_i2c_read(ctx->client, + TC9563_EMBEDDED_ETH_DELAY, + &rd_val); if (ret) return ret; if (is_l1) - rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L1_DELAY_MASK); + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L1_DELAY_MASK); else - rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK); + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L0S_DELAY_MASK); - return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_EMBEDDED_ETH_DELAY, rd_val); + return tc9563_pwrctrl_i2c_write(ctx->client, + TC9563_EMBEDDED_ETH_DELAY, + rd_val); } - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port)); + ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, + BIT(port)); if (ret) return ret; return tc9563_pwrctrl_i2c_write(ctx->client, - is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units); + is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, + units); } static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, @@ -309,7 +322,8 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, {TC9563_TX_MARGIN, amp}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq)); + return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, + ARRAY_SIZE(tx_amp_seq)); } static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, @@ -352,8 +366,8 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, {TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, - disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq)); + return tc9563_pwrctrl_i2c_bulk_write(ctx->client, disable_dfe_seq, + ARRAY_SIZE(disable_dfe_seq)); } static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, @@ -368,18 +382,22 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, if (!nfts[0]) return 0; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port)); + ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, + BIT(port)); if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, ARRAY_SIZE(nfts_seq)); + return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, + ARRAY_SIZE(nfts_seq)); } -static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, bool deassert) +static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, + bool deassert) { int ret, val; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK); + ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, + TC9563_GPIO_MASK); if (ret) return ret; @@ -388,7 +406,8 @@ static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val); } -static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct device_node *node, +static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, + struct device_node *node, enum tc9563_pwrctrl_ports port) { struct tc9563_pwrctrl_cfg *cfg; @@ -534,7 +553,8 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) for (int i = 0; i < TC9563_PWRCTL_MAX_SUPPLY; i++) ctx->supplies[i].supply = tc9563_supply_names[i]; - ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, ctx->supplies); + ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, + ctx->supplies); if (ret) { dev_err_probe(dev, ret, "failed to get supply regulator\n"); @@ -558,7 +578,8 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) /* * Downstream ports are always children of the upstream port. - * The first node represents DSP1, the second node represents DSP2, and so on. + * The first node represents DSP1, the second node represents DSP2, + * and so on. */ for_each_child_of_node_scoped(pdev->dev.of_node, child) { ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port++); @@ -567,7 +588,9 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) /* Embedded ethernet device are under DSP3 */ if (port == TC9563_DSP3) for_each_child_of_node_scoped(child, child1) { - ret = tc9563_pwrctrl_parse_device_dt(ctx, child1, port++); + port++; + ret = tc9563_pwrctrl_parse_device_dt(ctx, + child1, port); if (ret) break; } From 8d10f6a87580cb462d40dac473f4fc3b372337af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jan 2026 12:58:57 +0530 Subject: [PATCH 05/12] FROMLIST: PCI/pwrctrl: tc9563: Add local variables to reduce repetition Add local struct device * and struct device_node * variables to reduce repetitive pointer chasing. No functional changes intended. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-3-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam --- drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index b51947fa9300c..032d230eb1511 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -451,12 +451,13 @@ static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx) static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) { + struct device *dev = ctx->pwrctrl.dev; struct tc9563_pwrctrl_cfg *cfg; int ret, i; ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) - return dev_err_probe(ctx->pwrctrl.dev, ret, "cannot enable regulators\n"); + return dev_err_probe(dev, ret, "cannot enable regulators\n"); gpiod_set_value(ctx->reset_gpio, 0); @@ -474,37 +475,37 @@ static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) cfg = &ctx->cfg[i]; ret = tc9563_pwrctrl_disable_port(ctx, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Disabling port failed\n"); + dev_err(dev, "Disabling port failed\n"); goto power_off; } ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting L0s entry delay failed\n"); + dev_err(dev, "Setting L0s entry delay failed\n"); goto power_off; } ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting L1 entry delay failed\n"); + dev_err(dev, "Setting L1 entry delay failed\n"); goto power_off; } ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i, cfg->tx_amp); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting Tx amplitude failed\n"); + dev_err(dev, "Setting Tx amplitude failed\n"); goto power_off; } ret = tc9563_pwrctrl_set_nfts(ctx, i, cfg->nfts); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting N_FTS failed\n"); + dev_err(dev, "Setting N_FTS failed\n"); goto power_off; } ret = tc9563_pwrctrl_disable_dfe(ctx, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Disabling DFE failed\n"); + dev_err(dev, "Disabling DFE failed\n"); goto power_off; } } @@ -521,6 +522,7 @@ static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) static int tc9563_pwrctrl_probe(struct platform_device *pdev) { struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent); + struct device_node *node = pdev->dev.of_node; struct pci_dev *pci_dev = to_pci_dev(pdev->dev.parent); struct pci_bus *bus = bridge->bus; struct device *dev = &pdev->dev; @@ -533,7 +535,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - ret = of_property_read_u32_index(pdev->dev.of_node, "i2c-parent", 1, &addr); + ret = of_property_read_u32_index(node, "i2c-parent", 1, &addr); if (ret) return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n"); @@ -570,7 +572,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) pci_pwrctrl_init(&ctx->pwrctrl, dev); port = TC9563_USP; - ret = tc9563_pwrctrl_parse_device_dt(ctx, pdev->dev.of_node, port); + ret = tc9563_pwrctrl_parse_device_dt(ctx, node, port); if (ret) { dev_err(dev, "failed to parse device tree properties: %d\n", ret); goto remove_i2c; @@ -581,7 +583,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) * The first node represents DSP1, the second node represents DSP2, * and so on. */ - for_each_child_of_node_scoped(pdev->dev.of_node, child) { + for_each_child_of_node_scoped(node, child) { ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port++); if (ret) break; From affcef2c92493464806b479a1b3178d5e127e61a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jan 2026 12:58:58 +0530 Subject: [PATCH 06/12] FROMLIST: PCI/pwrctrl: tc9563: Rename private struct and pointers for consistency Previously the pwrseq, tc9563, and slot pwrctrl drivers used different naming conventions for their private data structs and pointers to them, which makes patches hard to read: Previous names New names ------------------------------------ ---------------------------------- struct pci_pwrctrl_pwrseq_data { struct pci_pwrctrl_pwrseq { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_pwrseq_data *data struct pci_pwrctrl_pwrseq *pwrseq struct tc9563_pwrctrl_ctx { struct pci_pwrctrl_tc9563 { struct tc9563_pwrctrl_ctx *ctx struct pci_pwrctrl_tc9563 *tc9563 struct pci_pwrctrl_slot_data { struct pci_pwrctrl_slot { struct pci_pwrctrl ctx; struct pci_pwrctrl pwrctrl; struct pci_pwrctrl_slot_data *slot struct pci_pwrctrl_slot *slot Rename "struct tc9563_pwrctrl_ctx" to "pci_pwrctrl_tc9563". Move the struct pci_pwrctrl member to be the first element in struct pci_pwrctrl_tc9563, as it is in the other drivers. Rename pointers from "struct tc9563_pwrctrl_ctx *ctx" to "struct pci_pwrctrl_tc9563 *tc9563". No functional change intended. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-6-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Bjorn Helgaas Signed-off-by: Manivannan Sadhasivam --- drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 142 ++++++++++++----------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index 032d230eb1511..c8cd15f3f5124 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -97,13 +97,13 @@ static const char *const tc9563_supply_names[TC9563_PWRCTL_MAX_SUPPLY] = { "vddio18", }; -struct tc9563_pwrctrl_ctx { +struct pci_pwrctrl_tc9563 { + struct pci_pwrctrl pwrctrl; struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY]; struct tc9563_pwrctrl_cfg cfg[TC9563_MAX]; struct gpio_desc *reset_gpio; struct i2c_adapter *adapter; struct i2c_client *client; - struct pci_pwrctrl pwrctrl; }; /* @@ -223,10 +223,10 @@ static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client, return 0; } -static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_disable_port(struct pci_pwrctrl_tc9563 *tc9563, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port]; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; const struct tc9563_pwrctrl_reg_setting *seq; int ret, len; @@ -241,15 +241,15 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx, len = ARRAY_SIZE(dsp2_pwroff_seq); } - ret = tc9563_pwrctrl_i2c_bulk_write(ctx->client, seq, len); + ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len); if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, common_pwroff_seq, + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq)); } -static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct pci_pwrctrl_tc9563 *tc9563, enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns) { @@ -263,7 +263,7 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, units = ns / 256; if (port == TC9563_ETHERNET) { - ret = tc9563_pwrctrl_i2c_read(ctx->client, + ret = tc9563_pwrctrl_i2c_read(tc9563->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val); if (ret) @@ -276,24 +276,25 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK); - return tc9563_pwrctrl_i2c_write(ctx->client, + return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_EMBEDDED_ETH_DELAY, rd_val); } - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, BIT(port)); if (ret) return ret; - return tc9563_pwrctrl_i2c_write(ctx->client, + return tc9563_pwrctrl_i2c_write(tc9563->client, is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units); } -static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_set_tx_amplitude(struct pci_pwrctrl_tc9563 *tc9563, enum tc9563_pwrctrl_ports port, u32 amp) { + u32 amp = tc9563->cfg[port].tx_amp; int port_access; if (amp < TC9563_TX_MARGIN_MIN_VAL) @@ -322,14 +323,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, {TC9563_TX_MARGIN, amp}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq)); } -static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_disable_dfe(struct pci_pwrctrl_tc9563 *tc9563, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port]; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; int port_access, lane_access = 0x3; u32 phy_rate = 0x21; @@ -366,13 +367,14 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, {TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, disable_dfe_seq, + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq)); } -static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_set_nfts(struct pci_pwrctrl_tc9563 *tc9563, enum tc9563_pwrctrl_ports port, u8 *nfts) { + u8 *nfts = tc9563->cfg[port].nfts; struct tc9563_pwrctrl_reg_setting nfts_seq[] = { {TC9563_NFTS_2_5_GT, nfts[0]}, {TC9563_NFTS_5_GT, nfts[1]}, @@ -382,35 +384,35 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, if (!nfts[0]) return 0; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, BIT(port)); if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq, ARRAY_SIZE(nfts_seq)); } -static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_assert_deassert_reset(struct pci_pwrctrl_tc9563 *tc9563, bool deassert) { int ret, val; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK); if (ret) return ret; val = deassert ? 0xc : 0; - return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val); + return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val); } -static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_parse_device_dt(struct pci_pwrctrl_tc9563 *tc9563, struct device_node *node, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; int ret; cfg = &ctx->cfg[port]; @@ -442,24 +444,25 @@ static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, return 0; } -static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx) +static void tc9563_pwrctrl_power_off(struct pci_pwrctrl_tc9563 *tc9563) { - gpiod_set_value(ctx->reset_gpio, 1); + gpiod_set_value(tc9563->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies); } -static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) +static int tc9563_pwrctrl_bring_up(struct pci_pwrctrl_tc9563 *tc9563) { - struct device *dev = ctx->pwrctrl.dev; + struct device *dev = tc9563->pwrctrl.dev; struct tc9563_pwrctrl_cfg *cfg; int ret, i; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies), + tc9563->supplies); if (ret < 0) return dev_err_probe(dev, ret, "cannot enable regulators\n"); - gpiod_set_value(ctx->reset_gpio, 0); + gpiod_set_value(tc9563->reset_gpio, 0); /* * From TC9563 PORSYS rev 0.2, figure 1.1 POR boot sequence @@ -467,55 +470,55 @@ static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) */ usleep_range(10000, 10500); - ret = tc9563_pwrctrl_assert_deassert_reset(ctx, false); + ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, false); if (ret) goto power_off; for (i = 0; i < TC9563_MAX; i++) { - cfg = &ctx->cfg[i]; - ret = tc9563_pwrctrl_disable_port(ctx, i); + cfg = &tc9563->cfg[i]; + ret = tc9563_pwrctrl_disable_port(tc9563, i); if (ret) { dev_err(dev, "Disabling port failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay); + ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay); if (ret) { dev_err(dev, "Setting L0s entry delay failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay); + ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay); if (ret) { dev_err(dev, "Setting L1 entry delay failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i, cfg->tx_amp); + ret = tc9563_pwrctrl_set_tx_amplitude(tc9563, i); if (ret) { dev_err(dev, "Setting Tx amplitude failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_nfts(ctx, i, cfg->nfts); + ret = tc9563_pwrctrl_set_nfts(tc9563, i); if (ret) { dev_err(dev, "Setting N_FTS failed\n"); goto power_off; } - ret = tc9563_pwrctrl_disable_dfe(ctx, i); + ret = tc9563_pwrctrl_disable_dfe(tc9563, i); if (ret) { dev_err(dev, "Disabling DFE failed\n"); goto power_off; } } - ret = tc9563_pwrctrl_assert_deassert_reset(ctx, true); + ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true); if (!ret) return 0; power_off: - tc9563_pwrctrl_power_off(ctx); + tc9563_pwrctrl_power_off(tc9563); return ret; } @@ -527,12 +530,12 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) struct pci_bus *bus = bridge->bus; struct device *dev = &pdev->dev; enum tc9563_pwrctrl_ports port; - struct tc9563_pwrctrl_ctx *ctx; + struct pci_pwrctrl_tc9563 *tc9563; struct device_node *i2c_node; int ret, addr; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) + tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL); + if (!tc9563) return -ENOMEM; ret = of_property_read_u32_index(node, "i2c-parent", 1, &addr); @@ -540,39 +543,39 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n"); i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0); - ctx->adapter = of_find_i2c_adapter_by_node(i2c_node); + tc9563->adapter = of_find_i2c_adapter_by_node(i2c_node); of_node_put(i2c_node); - if (!ctx->adapter) + if (!tc9563->adapter) return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n"); - ctx->client = i2c_new_dummy_device(ctx->adapter, addr); - if (IS_ERR(ctx->client)) { + tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr); + if (IS_ERR(tc9563->client)) { dev_err(dev, "Failed to create I2C client\n"); - put_device(&ctx->adapter->dev); - return PTR_ERR(ctx->client); + put_device(&tc9563->adapter->dev); + return PTR_ERR(tc9563->client); } for (int i = 0; i < TC9563_PWRCTL_MAX_SUPPLY; i++) - ctx->supplies[i].supply = tc9563_supply_names[i]; + tc9563->supplies[i].supply = tc9563_supply_names[i]; ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, - ctx->supplies); + tc9563->supplies); if (ret) { dev_err_probe(dev, ret, "failed to get supply regulator\n"); goto remove_i2c; } - ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(ctx->reset_gpio)) { - ret = dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "failed to get reset GPIO\n"); + tc9563->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH); + if (IS_ERR(tc9563->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(tc9563->reset_gpio), "failed to get resx GPIO\n"); goto remove_i2c; } - pci_pwrctrl_init(&ctx->pwrctrl, dev); + pci_pwrctrl_init(&tc9563->pwrctrl, dev); port = TC9563_USP; - ret = tc9563_pwrctrl_parse_device_dt(ctx, node, port); + ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port); if (ret) { dev_err(dev, "failed to parse device tree properties: %d\n", ret); goto remove_i2c; @@ -584,14 +587,15 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) * and so on. */ for_each_child_of_node_scoped(node, child) { - ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port++); + port++; + ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port); if (ret) break; /* Embedded ethernet device are under DSP3 */ if (port == TC9563_DSP3) for_each_child_of_node_scoped(child, child1) { port++; - ret = tc9563_pwrctrl_parse_device_dt(ctx, + ret = tc9563_pwrctrl_parse_device_dt(tc9563, child1, port); if (ret) break; @@ -605,7 +609,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) if (!pcie_link_is_active(pci_dev) && bridge->ops->stop_link) bridge->ops->stop_link(bus); - ret = tc9563_pwrctrl_bring_up(ctx); + ret = tc9563_pwrctrl_bring_up(tc9563); if (ret) goto remove_i2c; @@ -615,29 +619,29 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) goto power_off; } - ret = devm_pci_pwrctrl_device_set_ready(dev, &ctx->pwrctrl); + ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl); if (ret) goto power_off; - platform_set_drvdata(pdev, ctx); + platform_set_drvdata(pdev, tc9563); return 0; power_off: - tc9563_pwrctrl_power_off(ctx); + tc9563_pwrctrl_power_off(tc9563); remove_i2c: - i2c_unregister_device(ctx->client); - put_device(&ctx->adapter->dev); + i2c_unregister_device(tc9563->client); + put_device(&tc9563->adapter->dev); return ret; } static void tc9563_pwrctrl_remove(struct platform_device *pdev) { - struct tc9563_pwrctrl_ctx *ctx = platform_get_drvdata(pdev); + struct pci_pwrctrl_tc9563 *tc9563 = platform_get_drvdata(pdev); - tc9563_pwrctrl_power_off(ctx); - i2c_unregister_device(ctx->client); - put_device(&ctx->adapter->dev); + tc9563_pwrctrl_power_off(tc9563); + i2c_unregister_device(tc9563->client); + put_device(&tc9563->adapter->dev); } static const struct of_device_id tc9563_pwrctrl_of_match[] = { From 0f61a1be165804e1f540c1fdceb3cdd9c0fda7f2 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:58:59 +0530 Subject: [PATCH 07/12] FROMLIST: PCI/pwrctrl: slot: Factor out power on/off code to helpers In order to allow the pwrctrl core to control the power on/off logic of the pwrctrl slot driver, move the power on/off code to pci_pwrctrl_slot_power_{off/on} helper functions. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-7-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Manivannan Sadhasivam --- drivers/pci/pwrctrl/slot.c | 49 +++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 5ddae4ae34315..5d0ec880c0ecf 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -17,13 +17,40 @@ struct pci_pwrctrl_slot { struct pci_pwrctrl pwrctrl; struct regulator_bulk_data *supplies; int num_supplies; + struct clk *clk; }; -static void devm_pci_pwrctrl_slot_power_off(void *data) +static int pci_pwrctrl_slot_power_on(struct pci_pwrctrl *pwrctrl) { - struct pci_pwrctrl_slot *slot = data; + struct pci_pwrctrl_slot *slot = container_of(pwrctrl, + struct pci_pwrctrl_slot, pwrctrl); + int ret; + + ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); + if (ret < 0) { + dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n"); + return ret; + } + + return clk_prepare_enable(slot->clk); +} + +static int pci_pwrctrl_slot_power_off(struct pci_pwrctrl *pwrctrl) +{ + struct pci_pwrctrl_slot *slot = container_of(pwrctrl, + struct pci_pwrctrl_slot, pwrctrl); regulator_bulk_disable(slot->num_supplies, slot->supplies); + clk_disable_unprepare(slot->clk); + + return 0; +} + +static void devm_pci_pwrctrl_slot_release(void *data) +{ + struct pci_pwrctrl_slot *slot = data; + + pci_pwrctrl_slot_power_off(&slot->pwrctrl); regulator_bulk_free(slot->num_supplies, slot->supplies); } @@ -31,7 +58,6 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) { struct pci_pwrctrl_slot *slot; struct device *dev = &pdev->dev; - struct clk *clk; int ret; slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL); @@ -46,23 +72,18 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) } slot->num_supplies = ret; - ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to enable slot regulators\n"); - regulator_bulk_free(slot->num_supplies, slot->supplies); - return ret; - } - ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off, + ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_release, slot); if (ret) return ret; - clk = devm_clk_get_optional_enabled(dev, NULL); - if (IS_ERR(clk)) { - return dev_err_probe(dev, PTR_ERR(clk), + slot->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(slot->clk)) + return dev_err_probe(dev, PTR_ERR(slot->clk), "Failed to enable slot clock\n"); - } + + pci_pwrctrl_slot_power_on(&slot->pwrctrl); pci_pwrctrl_init(&slot->pwrctrl, dev); From 6fa9aeb7a1874e5f075bef1a4da67ea485ab23dc Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:59:00 +0530 Subject: [PATCH 08/12] FROMLIST: PCI/pwrctrl: pwrseq: Factor out power on/off code to helpers In order to allow the pwrctrl core to control the power on/off logic of the pwrctrl pwrseq driver, move the power on/off code to pci_pwrctrl_pwrseq_power_{off/on} helper functions. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-8-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Manivannan Sadhasivam --- drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index c0d22dc3a8561..d2c261b09030f 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -52,11 +52,27 @@ static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata = .validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device, }; +static int pci_pwrctrl_pwrseq_power_on(struct pci_pwrctrl *pwrctrl) +{ + struct pci_pwrctrl_pwrseq *pwrseq = container_of(pwrctrl, + struct pci_pwrctrl_pwrseq, pwrctrl); + + return pwrseq_power_on(pwrseq->pwrseq); +} + +static int pci_pwrctrl_pwrseq_power_off(struct pci_pwrctrl *pwrctrl) +{ + struct pci_pwrctrl_pwrseq *pwrseq = container_of(pwrctrl, + struct pci_pwrctrl_pwrseq, pwrctrl); + + return pwrseq_power_off(pwrseq->pwrseq); +} + static void devm_pci_pwrctrl_pwrseq_power_off(void *data) { - struct pwrseq_desc *pwrseq = data; + struct pci_pwrctrl_pwrseq *pwrseq = data; - pwrseq_power_off(pwrseq); + pci_pwrctrl_pwrseq_power_off(&pwrseq->pwrctrl); } static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) @@ -85,13 +101,13 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq), "Failed to get the power sequencer\n"); - ret = pwrseq_power_on(pwrseq->pwrseq); + ret = pci_pwrctrl_pwrseq_power_on(&pwrseq->pwrctrl); if (ret) return dev_err_probe(dev, ret, "Failed to power-on the device\n"); ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off, - pwrseq->pwrseq); + pwrseq); if (ret) return ret; From d3233ea226e6ed3fd0c12c2647c9449f9ac5d50f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:59:01 +0530 Subject: [PATCH 09/12] FROMLIST: PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks To allow the pwrctrl core to control the power on/off sequences of the pwrctrl drivers, add the 'struct pci_pwrctrl::power_{on/off}' callbacks and populate them in the respective pwrctrl drivers. The pwrctrl drivers still power on the resources on their own now. So there is no functional change. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-9-9d26da3ce903@oss.qualcomm.com/ Co-developed-by: Krishna Chaitanya Chundru Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Chen-Yu Tsai Reviewed-by: Bartosz Golaszewski --- drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 3 +++ drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 22 ++++++++++++++++------ drivers/pci/pwrctrl/slot.c | 3 +++ include/linux/pci-pwrctrl.h | 4 ++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index d2c261b09030f..2ee02edd55a31 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -111,6 +111,9 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) if (ret) return ret; + pwrseq->pwrctrl.power_on = pci_pwrctrl_pwrseq_power_on; + pwrseq->pwrctrl.power_off = pci_pwrctrl_pwrseq_power_off; + pci_pwrctrl_init(&pwrseq->pwrctrl, dev); ret = devm_pci_pwrctrl_device_set_ready(dev, &pwrseq->pwrctrl); diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index c8cd15f3f5124..4067eba256364 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -444,15 +444,22 @@ static int tc9563_pwrctrl_parse_device_dt(struct pci_pwrctrl_tc9563 *tc9563, return 0; } -static void tc9563_pwrctrl_power_off(struct pci_pwrctrl_tc9563 *tc9563) +static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl) { + struct pci_pwrctrl_tc9563 *tc9563 = container_of(pwrctrl, + struct pci_pwrctrl_tc9563, pwrctrl); + gpiod_set_value(tc9563->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies); + + return 0; } -static int tc9563_pwrctrl_bring_up(struct pci_pwrctrl_tc9563 *tc9563) +static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) { + struct pci_pwrctrl_tc9563 *tc9563 = container_of(pwrctrl, + struct pci_pwrctrl_tc9563, pwrctrl); struct device *dev = tc9563->pwrctrl.dev; struct tc9563_pwrctrl_cfg *cfg; int ret, i; @@ -518,7 +525,7 @@ static int tc9563_pwrctrl_bring_up(struct pci_pwrctrl_tc9563 *tc9563) return 0; power_off: - tc9563_pwrctrl_power_off(tc9563); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); return ret; } @@ -609,7 +616,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) if (!pcie_link_is_active(pci_dev) && bridge->ops->stop_link) bridge->ops->stop_link(bus); - ret = tc9563_pwrctrl_bring_up(tc9563); + ret = tc9563_pwrctrl_power_on(&tc9563->pwrctrl); if (ret) goto remove_i2c; @@ -619,6 +626,9 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) goto power_off; } + tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on; + tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off; + ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl); if (ret) goto power_off; @@ -628,7 +638,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) return 0; power_off: - tc9563_pwrctrl_power_off(tc9563); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); remove_i2c: i2c_unregister_device(tc9563->client); put_device(&tc9563->adapter->dev); @@ -639,7 +649,7 @@ static void tc9563_pwrctrl_remove(struct platform_device *pdev) { struct pci_pwrctrl_tc9563 *tc9563 = platform_get_drvdata(pdev); - tc9563_pwrctrl_power_off(tc9563); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); i2c_unregister_device(tc9563->client); put_device(&tc9563->adapter->dev); } diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 5d0ec880c0ecf..55828aec24862 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -85,6 +85,9 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) pci_pwrctrl_slot_power_on(&slot->pwrctrl); + slot->pwrctrl.power_on = pci_pwrctrl_slot_power_on; + slot->pwrctrl.power_off = pci_pwrctrl_slot_power_off; + pci_pwrctrl_init(&slot->pwrctrl, dev); ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl); diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h index 4aefc7901cd18..435b822c841ea 100644 --- a/include/linux/pci-pwrctrl.h +++ b/include/linux/pci-pwrctrl.h @@ -31,6 +31,8 @@ struct device_link; /** * struct pci_pwrctrl - PCI device power control context. * @dev: Address of the power controlling device. + * @power_on: Callback to power on the power controlling device. + * @power_off: Callback to power off the power controlling device. * * An object of this type must be allocated by the PCI power control device and * passed to the pwrctrl subsystem to trigger a bus rescan and setup a device @@ -38,6 +40,8 @@ struct device_link; */ struct pci_pwrctrl { struct device *dev; + int (*power_on)(struct pci_pwrctrl *pwrctrl); + int (*power_off)(struct pci_pwrctrl *pwrctrl); /* private: internal use only */ struct notifier_block nb; From 905c43bf621b2ac4e2b151baef36b477286633d4 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Thu, 15 Jan 2026 12:59:02 +0530 Subject: [PATCH 10/12] FROMLIST: PCI/pwrctrl: Add APIs to create, destroy pwrctrl devices Previously, the PCI core created pwrctrl devices during pci_scan_device() on its own and then skipped enumeration of those devices, hoping the pwrctrl driver would power them on and trigger a bus rescan. This approach works for endpoint devices directly connected to Root Ports, but it fails for PCIe switches acting as bus extenders. When the switch requires pwrctrl support and the pwrctrl driver is not available during the pwrctrl device creation, its enumeration will be skipped during the initial PCI bus scan. This premature scan leads the PCI core to allocate resources (bridge windows, bus numbers) for the upstream bridge based on available downstream buses at scan time. For non-hotplug capable bridges, PCI core typically allocates resources based on the number of buses available during the initial bus scan, which happens to be just one if the switch is not powered on and enumerated at that time. When the switch gets enumerated later on, it will fail due to the lack of upstream resources. As a result, a PCIe switch powered on by the pwrctrl driver cannot be reliably enumerated currently. Either the switch has to be enabled in the bootloader or the switch pwrctrl driver has to be loaded during the pwrctrl device creation time to work around these issues. Introduce new APIs to explicitly create and destroy pwrctrl devices from controller drivers by recursively scanning the PCI child nodes of the controller. These APIs allow creating pwrctrl devices based on the original criteria and are intended to be called during controller probe and removal. These APIs, together with the upcoming APIs for power on/off will allow the controller drivers to power on all the devices before starting the initial bus scan, thereby solving the resource allocation issue. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-10-9d26da3ce903@oss.qualcomm.com/ Signed-off-by: Krishna Chaitanya Chundru [mani: splitted the patch, cleaned up the code, and rewrote description] Reviewed-by: Bartosz Golaszewski Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Chen-Yu Tsai --- drivers/pci/of.c | 1 + drivers/pci/pwrctrl/core.c | 114 ++++++++++++++++++++++++++++++++++++ include/linux/pci-pwrctrl.h | 8 ++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 3579265f11984..9bb5f258759be 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -867,6 +867,7 @@ bool of_pci_supply_present(struct device_node *np) return false; } +EXPORT_SYMBOL_GPL(of_pci_supply_present); #endif /* CONFIG_PCI */ diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 6bdbfed584d6d..b423768cc477c 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -3,14 +3,21 @@ * Copyright (C) 2024 Linaro Ltd. */ +#define dev_fmt(fmt) "pwrctrl: " fmt + #include #include #include +#include +#include #include #include +#include #include #include +#include "../pci.h" + static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -145,6 +152,113 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +static int pci_pwrctrl_create_device(struct device_node *np, + struct device *parent) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) { + ret = pci_pwrctrl_create_device(child, parent); + if (ret) + return ret; + } + + /* Bail out if the platform device is already available for the node */ + pdev = of_find_device_by_node(np); + if (pdev) { + platform_device_put(pdev); + return 0; + } + + /* + * Sanity check to make sure that the node has the compatible property + * to allow driver binding. + */ + if (!of_property_present(np, "compatible")) + return 0; + + /* + * Check whether the pwrctrl device really needs to be created or not. + * This is decided based on at least one of the power supplies being + * defined in the devicetree node of the device. + */ + if (!of_pci_supply_present(np)) { + dev_dbg(parent, "Skipping OF node: %s\n", np->name); + return 0; + } + + /* Now create the pwrctrl device */ + pdev = of_platform_device_create(np, NULL, parent); + if (!pdev) { + dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name); + return -EINVAL; + } + + return 0; +} + +/** + * pci_pwrctrl_create_devices - Create pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively create pwrctrl devices for the devicetree hierarchy below + * the specified PCI host controller in a depth first manner. On error, all + * created devices will be destroyed. + * + * Return: 0 on success, negative error number on error. + */ +int pci_pwrctrl_create_devices(struct device *parent) +{ + int ret; + + for_each_available_child_of_node_scoped(parent->of_node, child) { + ret = pci_pwrctrl_create_device(child, parent); + if (ret) { + pci_pwrctrl_destroy_devices(parent); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices); + +static void pci_pwrctrl_destroy_device(struct device_node *np) +{ + struct platform_device *pdev; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_destroy_device(child); + + pdev = of_find_device_by_node(np); + if (!pdev) + return; + + of_device_unregister(pdev); + platform_device_put(pdev); + + of_node_clear_flag(np, OF_POPULATED); +} + +/** + * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively destroy pwrctrl devices for the devicetree hierarchy below + * the specified PCI host controller in a depth first manner. + */ +void pci_pwrctrl_destroy_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_destroy_device(child); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices); + MODULE_AUTHOR("Bartosz Golaszewski "); MODULE_DESCRIPTION("PCI Device Power Control core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h index 435b822c841ea..44f66872d0901 100644 --- a/include/linux/pci-pwrctrl.h +++ b/include/linux/pci-pwrctrl.h @@ -54,5 +54,11 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl); void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl); int devm_pci_pwrctrl_device_set_ready(struct device *dev, struct pci_pwrctrl *pwrctrl); - +#if IS_ENABLED(CONFIG_PCI_PWRCTRL) +int pci_pwrctrl_create_devices(struct device *parent); +void pci_pwrctrl_destroy_devices(struct device *parent); +#else +static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; } +static void pci_pwrctrl_destroy_devices(struct device *parent) { } +#endif #endif /* __PCI_PWRCTRL_H__ */ From d818f71cbe1cf15d74751d6c40ebc3ca970ca170 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:59:03 +0530 Subject: [PATCH 11/12] FROMLIST: PCI/pwrctrl: Add APIs to power on/off pwrctrl devices To fix bridge resource allocation issues when powering PCI bridges with the pwrctrl driver, introduce APIs to explicitly power on and off all related devices simultaneously. Previously, the individual pwrctrl drivers powered on/off the PCI devices autonomously, without any control from the controller drivers. But to enforce ordering with respect to powering on the devices, these APIs will power on/off all the devices at the same time. The pci_pwrctrl_power_on_devices() API recursively scans the PCI child nodes, makes sure that pwrctrl drivers are bound to devices, and calls their power_on() callbacks. If any pwrctrl driver is not bound, it will return -EPROBE_DEFER. Similarly, pci_pwrctrl_power_off_devices() API powers off devices recursively via their power_off() callbacks. These APIs are expected to be called during the controller probe and suspend/resume time to power on/off the devices. But before calling these APIs, the pwrctrl devices should be created using the pci_pwrctrl_{create/destroy}_devices() APIs. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-11-9d26da3ce903@oss.qualcomm.com/ Co-developed-by: Krishna Chaitanya Chundru Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam [bhelgaas: return early when !pdev and unindent, move ctx.power_on/off here] Signed-off-by: Bjorn Helgaas Tested-by: Chen-Yu Tsai Reviewed-by: Bartosz Golaszewski --- drivers/pci/pwrctrl/core.c | 130 ++++++++++++++++++++++++++++++++++++ include/linux/pci-pwrctrl.h | 4 ++ 2 files changed, 134 insertions(+) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index b423768cc477c..fef5243d94458 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -65,6 +65,7 @@ void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev) { pwrctrl->dev = dev; INIT_WORK(&pwrctrl->work, rescan_work_func); + dev_set_drvdata(dev, pwrctrl); } EXPORT_SYMBOL_GPL(pci_pwrctrl_init); @@ -152,6 +153,135 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +static int __pci_pwrctrl_power_off_device(struct device *dev) +{ + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); + + if (!pwrctrl) + return 0; + + return pwrctrl->power_off(pwrctrl); +} + +static void pci_pwrctrl_power_off_device(struct device_node *np) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_power_off_device(child); + + pdev = of_find_device_by_node(np); + if (!pdev) + return; + + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_off_device(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to power off device: %d", ret); + } + + platform_device_put(pdev); +} + +/** + * pci_pwrctrl_power_off_devices - Power off pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively traverse all pwrctrl devices for the devicetree hierarchy + * below the specified PCI host controller and power them off in a depth + * first manner. + */ +void pci_pwrctrl_power_off_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_power_off_device(child); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices); + +static int __pci_pwrctrl_power_on_device(struct device *dev) +{ + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); + + if (!pwrctrl) + return 0; + + return pwrctrl->power_on(pwrctrl); +} + +/* + * Power on the devices in a depth first manner. Before powering on the device, + * make sure its driver is bound. + */ +static int pci_pwrctrl_power_on_device(struct device_node *np) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) { + ret = pci_pwrctrl_power_on_device(child); + if (ret) + return ret; + } + + pdev = of_find_device_by_node(np); + if (!pdev) + return 0; + + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_on_device(&pdev->dev); + } else { + /* FIXME: Use blocking wait instead of probe deferral */ + dev_dbg(&pdev->dev, "driver is not bound\n"); + ret = -EPROBE_DEFER; + } + + platform_device_put(pdev); + + return ret; +} + +/** + * pci_pwrctrl_power_on_devices - Power on pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively traverse all pwrctrl devices for the devicetree hierarchy + * below the specified PCI host controller and power them on in a depth + * first manner. On error, all powered on devices will be powered off. + * + * Return: 0 on success, -EPROBE_DEFER if any pwrctrl driver is not bound, an + * appropriate error code otherwise. + */ +int pci_pwrctrl_power_on_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + struct device_node *child = NULL; + int ret; + + for_each_available_child_of_node(np, child) { + ret = pci_pwrctrl_power_on_device(child); + if (ret) + goto err_power_off; + } + + return 0; + +err_power_off: + for_each_available_child_of_node_scoped(np, tmp) { + if (tmp == child) + break; + pci_pwrctrl_power_off_device(tmp); + } + of_node_put(child); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); + static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent) { diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h index 44f66872d0901..1192a2527521d 100644 --- a/include/linux/pci-pwrctrl.h +++ b/include/linux/pci-pwrctrl.h @@ -57,8 +57,12 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, #if IS_ENABLED(CONFIG_PCI_PWRCTRL) int pci_pwrctrl_create_devices(struct device *parent); void pci_pwrctrl_destroy_devices(struct device *parent); +int pci_pwrctrl_power_on_devices(struct device *parent); +void pci_pwrctrl_power_off_devices(struct device *parent); #else static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; } static void pci_pwrctrl_destroy_devices(struct device *parent) { } +static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; } +static void pci_pwrctrl_power_off_devices(struct device *parent) { } #endif #endif /* __PCI_PWRCTRL_H__ */ From cf9bc35bb88858b9d4bc2f91e82bbe27825b11d5 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 15 Jan 2026 12:59:04 +0530 Subject: [PATCH 12/12] FROMLIST: PCI/pwrctrl: Switch to pwrctrl create, power on/off, destroy APIs Adopt pwrctrl APIs to create, power on/off, and destroy pwrctrl devices. In qcom_pcie_host_init(), call pci_pwrctrl_create_devices() to create devices, then pci_pwrctrl_power_on_devices() to power them on, both after controller resource initialization. Once successful, deassert PERST# for all devices. In qcom_pcie_host_deinit(), call pci_pwrctrl_power_off_devices() after asserting PERST#. Note that pci_pwrctrl_destroy_devices() is not called here, as deinit is only invoked during system suspend where device destruction is unnecessary. If the driver becomes removable in future, pci_pwrctrl_destroy_devices() should be called in the remove() handler. Remove the old pwrctrl framework code from the PCI core (including devlinks) as the new APIs are now the sole consumer of pwrctrl functionality. And also do not power on the pwrctrl drivers during probe() as this is now handled by the APIs. Link: https://lore.kernel.org/all/20260115-pci-pwrctrl-rework-v5-12-9d26da3ce903@oss.qualcomm.com/ Co-developed-by: Krishna Chaitanya Chundru Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Tested-by: Chen-Yu Tsai Reviewed-by: Bartosz Golaszewski --- drivers/pci/bus.c | 19 -------- drivers/pci/controller/dwc/pcie-qcom.c | 24 +++++++++- drivers/pci/probe.c | 59 ------------------------ drivers/pci/pwrctrl/core.c | 15 ------ drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 5 -- drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 21 ++------- drivers/pci/pwrctrl/slot.c | 2 - drivers/pci/remove.c | 20 -------- 8 files changed, 25 insertions(+), 140 deletions(-) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index f26aec6ff5889..04bd9deefc8b5 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -343,7 +343,6 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } void pci_bus_add_device(struct pci_dev *dev) { struct device_node *dn = dev->dev.of_node; - struct platform_device *pdev; /* * Can not put in pci_device_add yet because resources @@ -357,24 +356,6 @@ void pci_bus_add_device(struct pci_dev *dev) pci_proc_attach_device(dev); pci_bridge_d3_update(dev); - /* - * If the PCI device is associated with a pwrctrl device with a - * power supply, create a device link between the PCI device and - * pwrctrl device. This ensures that pwrctrl drivers are probed - * before PCI client drivers. - */ - pdev = of_find_device_by_node(dn); - if (pdev) { - if (of_pci_supply_present(dn)) { - if (!device_link_add(&dev->dev, &pdev->dev, - DL_FLAG_AUTOREMOVE_CONSUMER)) { - pci_err(dev, "failed to add device link to power control device %s\n", - pdev->name); - } - } - put_device(&pdev->dev); - } - if (!dn || of_device_is_available(dn)) pci_dev_allow_binding(dev); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index d81a479e49ef2..6c7e46f70b736 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1328,10 +1329,18 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_deinit; + ret = pci_pwrctrl_create_devices(pci->dev); + if (ret) + goto err_disable_phy; + + ret = pci_pwrctrl_power_on_devices(pci->dev); + if (ret) + goto err_pwrctrl_destroy; + if (pcie->cfg->ops->post_init) { ret = pcie->cfg->ops->post_init(pcie); if (ret) - goto err_disable_phy; + goto err_pwrctrl_power_off; } qcom_ep_reset_deassert(pcie); @@ -1346,6 +1355,11 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) err_assert_reset: qcom_ep_reset_assert(pcie); +err_pwrctrl_power_off: + pci_pwrctrl_power_off_devices(pci->dev); +err_pwrctrl_destroy: + if (ret != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); err_deinit: @@ -1360,6 +1374,12 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) struct qcom_pcie *pcie = to_qcom_pcie(pci); qcom_ep_reset_assert(pcie); + + /* + * No need to destroy pwrctrl devices as this function only gets called + * during system suspend as of now. + */ + pci_pwrctrl_power_off_devices(pci->dev); qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -1968,7 +1988,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) ret = dw_pcie_host_init(pp); if (ret) { - dev_err(dev, "cannot initialize host\n"); + dev_err_probe(dev, ret, "cannot initialize host\n"); goto err_phy_exit; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9cd032dff31e5..4ba6b7b5ebfef 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2533,56 +2533,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); -#if IS_ENABLED(CONFIG_PCI_PWRCTRL) -static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) -{ - struct pci_host_bridge *host = pci_find_host_bridge(bus); - struct platform_device *pdev; - struct device_node *np; - - np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn); - if (!np) - return NULL; - - pdev = of_find_device_by_node(np); - if (pdev) { - put_device(&pdev->dev); - goto err_put_of_node; - } - - /* - * First check whether the pwrctrl device really needs to be created or - * not. This is decided based on at least one of the power supplies - * being defined in the devicetree node of the device. - */ - if (!of_pci_supply_present(np)) { - pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name); - goto err_put_of_node; - } - - /* Now create the pwrctrl device */ - pdev = of_platform_device_create(np, NULL, &host->dev); - if (!pdev) { - pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name); - goto err_put_of_node; - } - - of_node_put(np); - - return pdev; - -err_put_of_node: - of_node_put(np); - - return NULL; -} -#else -static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) -{ - return NULL; -} -#endif - /* * Read the config data for a PCI device, sanity-check it, * and fill in the dev structure. @@ -2592,15 +2542,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; - /* - * Create pwrctrl device (if required) for the PCI device to handle the - * power state. If the pwrctrl device is created, then skip scanning - * further as the pwrctrl core will rescan the bus after powering on - * the device. - */ - if (pci_pwrctrl_create_device(bus, devfn)) - return NULL; - if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index fef5243d94458..1b91375738a08 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -45,16 +45,6 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; } -static void rescan_work_func(struct work_struct *work) -{ - struct pci_pwrctrl *pwrctrl = container_of(work, - struct pci_pwrctrl, work); - - pci_lock_rescan_remove(); - pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus); - pci_unlock_rescan_remove(); -} - /** * pci_pwrctrl_init() - Initialize the PCI power control context struct * @@ -64,7 +54,6 @@ static void rescan_work_func(struct work_struct *work) void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev) { pwrctrl->dev = dev; - INIT_WORK(&pwrctrl->work, rescan_work_func); dev_set_drvdata(dev, pwrctrl); } EXPORT_SYMBOL_GPL(pci_pwrctrl_init); @@ -95,8 +84,6 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl) if (ret) return ret; - schedule_work(&pwrctrl->work); - return 0; } EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready); @@ -109,8 +96,6 @@ EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready); */ void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl) { - cancel_work_sync(&pwrctrl->work); - /* * We don't have to delete the link here. Typically, this function * is only called when the power control device is being detached. If diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index 2ee02edd55a31..1df0cd73b416e 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -101,11 +101,6 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq), "Failed to get the power sequencer\n"); - ret = pci_pwrctrl_pwrseq_power_on(&pwrseq->pwrctrl); - if (ret) - return dev_err_probe(dev, ret, - "Failed to power-on the device\n"); - ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off, pwrseq); if (ret) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index 4067eba256364..b2ddc6eba6f2c 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -531,10 +531,8 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) static int tc9563_pwrctrl_probe(struct platform_device *pdev) { - struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent); struct device_node *node = pdev->dev.of_node; struct pci_dev *pci_dev = to_pci_dev(pdev->dev.parent); - struct pci_bus *bus = bridge->bus; struct device *dev = &pdev->dev; enum tc9563_pwrctrl_ports port; struct pci_pwrctrl_tc9563 *tc9563; @@ -613,19 +611,6 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) goto remove_i2c; } - if (!pcie_link_is_active(pci_dev) && bridge->ops->stop_link) - bridge->ops->stop_link(bus); - - ret = tc9563_pwrctrl_power_on(&tc9563->pwrctrl); - if (ret) - goto remove_i2c; - - if (!pcie_link_is_active(pci_dev) && bridge->ops->start_link) { - ret = bridge->ops->start_link(bus); - if (ret) - goto power_off; - } - tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on; tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off; @@ -633,8 +618,6 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) if (ret) goto power_off; - platform_set_drvdata(pdev, tc9563); - return 0; power_off: @@ -647,7 +630,9 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) static void tc9563_pwrctrl_remove(struct platform_device *pdev) { - struct pci_pwrctrl_tc9563 *tc9563 = platform_get_drvdata(pdev); + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(&pdev->dev); + struct pci_pwrctrl_tc9563 *tc9563 = container_of(pwrctrl, + struct pci_pwrctrl_tc9563, pwrctrl); tc9563_pwrctrl_power_off(&tc9563->pwrctrl); i2c_unregister_device(tc9563->client); diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 55828aec24862..a36e5dd424427 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -83,8 +83,6 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(slot->clk), "Failed to enable slot clock\n"); - pci_pwrctrl_slot_power_on(&slot->pwrctrl); - slot->pwrctrl.power_on = pci_pwrctrl_slot_power_on; slot->pwrctrl.power_off = pci_pwrctrl_slot_power_off; diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index ce5c25adef551..2a22595f38af6 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -17,25 +17,6 @@ static void pci_free_resources(struct pci_dev *dev) } } -static void pci_pwrctrl_unregister(struct device *dev) -{ - struct device_node *np; - struct platform_device *pdev; - - np = dev_of_node(dev); - if (!np) - return; - - pdev = of_find_device_by_node(np); - if (!pdev) - return; - - of_device_unregister(pdev); - put_device(&pdev->dev); - - of_node_clear_flag(np, OF_POPULATED); -} - static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); @@ -66,7 +47,6 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_doe_destroy(dev); pcie_aspm_exit_link_state(dev); pci_bridge_d3_update(dev); - pci_pwrctrl_unregister(&dev->dev); pci_free_resources(dev); put_device(&dev->dev); }