Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'pwrseq-updates-for-v6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull power sequencing updates from Bartosz Golaszewski:
"One new driver and a small set of improvements as well as a fix to
power sequence unit naming.

New driver:
- add a power sequencing driver for the T-HEAD TH1520 GPU

Power sequencing core improvements:
- allow to compile the pwrseq drivers with COMPILE_TEST=y in order to
improve the build-test coverage
- add named defines for the possible return values of the .match()
callback and use it in the existing drivers instead of magic values

Fix:
- Fix the name of the bluetooth-enable unit for WCN6855"

* tag 'pwrseq-updates-for-v6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
power: sequencing: qcom-wcn: fix bluetooth-wifi copypasta for WCN6855
power: sequencing: thead-gpu: use new defines for match() return values
power: sequencing: qcom-wcn: use new defines for match() return values
power: sequencing: add defines for return values of the match() callback
power: sequencing: extend build coverage with COMPILE_TEST=y
power: sequencing: thead-gpu: add missing header
power: sequencing: Add T-HEAD TH1520 GPU power sequencer driver

+271 -9
+1
MAINTAINERS
··· 21434 21434 F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c 21435 21435 F: drivers/pinctrl/pinctrl-th1520.c 21436 21436 F: drivers/pmdomain/thead/ 21437 + F: drivers/power/sequencing/pwrseq-thead-gpu.c 21437 21438 F: drivers/reset/reset-th1520.c 21438 21439 F: include/dt-bindings/clock/thead,th1520-clk-ap.h 21439 21440 F: include/dt-bindings/power/thead,th1520-power.h
+9 -1
drivers/power/sequencing/Kconfig
··· 16 16 config POWER_SEQUENCING_QCOM_WCN 17 17 tristate "Qualcomm WCN family PMU driver" 18 18 default m if ARCH_QCOM 19 - depends on OF 19 + depends on OF || COMPILE_TEST 20 20 help 21 21 Say Y here to enable the power sequencing driver for Qualcomm 22 22 WCN Bluetooth/WLAN chipsets. ··· 26 26 former two share the power-up sequence which is executed by the PMU, 27 27 this driver is needed for correct power control or else we'd risk not 28 28 respecting the required delays between enabling Bluetooth and WLAN. 29 + 30 + config POWER_SEQUENCING_TH1520_GPU 31 + tristate "T-HEAD TH1520 GPU power sequencing driver" 32 + depends on (ARCH_THEAD && AUXILIARY_BUS) || COMPILE_TEST 33 + help 34 + Say Y here to enable the power sequencing driver for the TH1520 SoC 35 + GPU. This driver handles the complex clock and reset sequence 36 + required to power on the Imagination BXM GPU on this platform. 29 37 30 38 endif
+1
drivers/power/sequencing/Makefile
··· 4 4 pwrseq-core-y := core.o 5 5 6 6 obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o 7 + obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o
+3 -3
drivers/power/sequencing/core.c
··· 628 628 return 0; 629 629 630 630 ret = pwrseq->match(pwrseq, match_data->dev); 631 - if (ret <= 0) 631 + if (ret == PWRSEQ_NO_MATCH || ret < 0) 632 632 return ret; 633 633 634 634 /* We got the matching device, let's find the right target. */ ··· 651 651 652 652 match_data->desc->pwrseq = pwrseq_device_get(pwrseq); 653 653 654 - return 1; 654 + return PWRSEQ_MATCH_OK; 655 655 } 656 656 657 657 /** ··· 684 684 pwrseq_match_device); 685 685 if (ret < 0) 686 686 return ERR_PTR(ret); 687 - if (ret == 0) 687 + if (ret == PWRSEQ_NO_MATCH) 688 688 /* No device matched. */ 689 689 return ERR_PTR(-EPROBE_DEFER); 690 690
+5 -5
drivers/power/sequencing/pwrseq-qcom-wcn.c
··· 155 155 }; 156 156 157 157 static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = { 158 - .name = "wlan-enable", 158 + .name = "bluetooth-enable", 159 159 .deps = pwrseq_qcom_wcn6855_unit_deps, 160 160 .enable = pwrseq_qcom_wcn_bt_enable, 161 161 .disable = pwrseq_qcom_wcn_bt_disable, ··· 341 341 * device. 342 342 */ 343 343 if (!of_property_present(dev_node, "vddaon-supply")) 344 - return 0; 344 + return PWRSEQ_NO_MATCH; 345 345 346 346 struct device_node *reg_node __free(device_node) = 347 347 of_parse_phandle(dev_node, "vddaon-supply", 0); 348 348 if (!reg_node) 349 - return 0; 349 + return PWRSEQ_NO_MATCH; 350 350 351 351 /* 352 352 * `reg_node` is the PMU AON regulator, its parent is the `regulators` ··· 355 355 */ 356 356 if (!reg_node->parent || !reg_node->parent->parent || 357 357 reg_node->parent->parent != ctx->of_node) 358 - return 0; 358 + return PWRSEQ_NO_MATCH; 359 359 360 - return 1; 360 + return PWRSEQ_MATCH_OK; 361 361 } 362 362 363 363 static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
+249
drivers/power/sequencing/pwrseq-thead-gpu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * T-HEAD TH1520 GPU Power Sequencer Driver 4 + * 5 + * Copyright (c) 2025 Samsung Electronics Co., Ltd. 6 + * Author: Michal Wilczynski <m.wilczynski@samsung.com> 7 + * 8 + * This driver implements the power sequence for the Imagination BXM-4-64 9 + * GPU on the T-HEAD TH1520 SoC. The sequence requires coordinating resources 10 + * from both the sequencer's parent device node (clkgen_reset) and the GPU's 11 + * device node (clocks and core reset). 12 + * 13 + * The `match` function is used to acquire the GPU's resources when the 14 + * GPU driver requests the "gpu-power" sequence target. 15 + */ 16 + 17 + #include <linux/auxiliary_bus.h> 18 + #include <linux/clk.h> 19 + #include <linux/delay.h> 20 + #include <linux/module.h> 21 + #include <linux/of.h> 22 + #include <linux/pwrseq/provider.h> 23 + #include <linux/reset.h> 24 + #include <linux/slab.h> 25 + 26 + #include <dt-bindings/power/thead,th1520-power.h> 27 + 28 + struct pwrseq_thead_gpu_ctx { 29 + struct pwrseq_device *pwrseq; 30 + struct reset_control *clkgen_reset; 31 + struct device_node *aon_node; 32 + 33 + /* Consumer resources */ 34 + struct device_node *consumer_node; 35 + struct clk_bulk_data *clks; 36 + int num_clks; 37 + struct reset_control *gpu_reset; 38 + }; 39 + 40 + static int pwrseq_thead_gpu_enable(struct pwrseq_device *pwrseq) 41 + { 42 + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 43 + int ret; 44 + 45 + if (!ctx->clks || !ctx->gpu_reset) 46 + return -ENODEV; 47 + 48 + ret = clk_bulk_prepare_enable(ctx->num_clks, ctx->clks); 49 + if (ret) 50 + return ret; 51 + 52 + ret = reset_control_deassert(ctx->clkgen_reset); 53 + if (ret) 54 + goto err_disable_clks; 55 + 56 + /* 57 + * According to the hardware manual, a delay of at least 32 clock 58 + * cycles is required between de-asserting the clkgen reset and 59 + * de-asserting the GPU reset. Assuming a worst-case scenario with 60 + * a very high GPU clock frequency, a delay of 1 microsecond is 61 + * sufficient to ensure this requirement is met across all 62 + * feasible GPU clock speeds. 63 + */ 64 + udelay(1); 65 + 66 + ret = reset_control_deassert(ctx->gpu_reset); 67 + if (ret) 68 + goto err_assert_clkgen; 69 + 70 + return 0; 71 + 72 + err_assert_clkgen: 73 + reset_control_assert(ctx->clkgen_reset); 74 + err_disable_clks: 75 + clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks); 76 + return ret; 77 + } 78 + 79 + static int pwrseq_thead_gpu_disable(struct pwrseq_device *pwrseq) 80 + { 81 + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 82 + int ret = 0, err; 83 + 84 + if (!ctx->clks || !ctx->gpu_reset) 85 + return -ENODEV; 86 + 87 + err = reset_control_assert(ctx->gpu_reset); 88 + if (err) 89 + ret = err; 90 + 91 + err = reset_control_assert(ctx->clkgen_reset); 92 + if (err && !ret) 93 + ret = err; 94 + 95 + clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks); 96 + 97 + /* ret stores values of the first error code */ 98 + return ret; 99 + } 100 + 101 + static const struct pwrseq_unit_data pwrseq_thead_gpu_unit = { 102 + .name = "gpu-power-sequence", 103 + .enable = pwrseq_thead_gpu_enable, 104 + .disable = pwrseq_thead_gpu_disable, 105 + }; 106 + 107 + static const struct pwrseq_target_data pwrseq_thead_gpu_target = { 108 + .name = "gpu-power", 109 + .unit = &pwrseq_thead_gpu_unit, 110 + }; 111 + 112 + static const struct pwrseq_target_data *pwrseq_thead_gpu_targets[] = { 113 + &pwrseq_thead_gpu_target, 114 + NULL 115 + }; 116 + 117 + static int pwrseq_thead_gpu_match(struct pwrseq_device *pwrseq, 118 + struct device *dev) 119 + { 120 + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 121 + static const char *const clk_names[] = { "core", "sys" }; 122 + struct of_phandle_args pwr_spec; 123 + int i, ret; 124 + 125 + /* We only match the specific T-HEAD TH1520 GPU compatible */ 126 + if (!of_device_is_compatible(dev->of_node, "thead,th1520-gpu")) 127 + return PWRSEQ_NO_MATCH; 128 + 129 + ret = of_parse_phandle_with_args(dev->of_node, "power-domains", 130 + "#power-domain-cells", 0, &pwr_spec); 131 + if (ret) 132 + return PWRSEQ_NO_MATCH; 133 + 134 + /* Additionally verify consumer device has AON as power-domain */ 135 + if (pwr_spec.np != ctx->aon_node || pwr_spec.args[0] != TH1520_GPU_PD) { 136 + of_node_put(pwr_spec.np); 137 + return PWRSEQ_NO_MATCH; 138 + } 139 + 140 + of_node_put(pwr_spec.np); 141 + 142 + /* If a consumer is already bound, only allow a re-match from it */ 143 + if (ctx->consumer_node) 144 + return ctx->consumer_node == dev->of_node ? 145 + PWRSEQ_MATCH_OK : PWRSEQ_NO_MATCH; 146 + 147 + ctx->num_clks = ARRAY_SIZE(clk_names); 148 + ctx->clks = kcalloc(ctx->num_clks, sizeof(*ctx->clks), GFP_KERNEL); 149 + if (!ctx->clks) 150 + return -ENOMEM; 151 + 152 + for (i = 0; i < ctx->num_clks; i++) 153 + ctx->clks[i].id = clk_names[i]; 154 + 155 + ret = clk_bulk_get(dev, ctx->num_clks, ctx->clks); 156 + if (ret) 157 + goto err_free_clks; 158 + 159 + ctx->gpu_reset = reset_control_get_shared(dev, NULL); 160 + if (IS_ERR(ctx->gpu_reset)) { 161 + ret = PTR_ERR(ctx->gpu_reset); 162 + goto err_put_clks; 163 + } 164 + 165 + ctx->consumer_node = of_node_get(dev->of_node); 166 + 167 + return PWRSEQ_MATCH_OK; 168 + 169 + err_put_clks: 170 + clk_bulk_put(ctx->num_clks, ctx->clks); 171 + err_free_clks: 172 + kfree(ctx->clks); 173 + ctx->clks = NULL; 174 + 175 + return ret; 176 + } 177 + 178 + static int pwrseq_thead_gpu_probe(struct auxiliary_device *adev, 179 + const struct auxiliary_device_id *id) 180 + { 181 + struct device *dev = &adev->dev; 182 + struct device *parent_dev = dev->parent; 183 + struct pwrseq_thead_gpu_ctx *ctx; 184 + struct pwrseq_config config = {}; 185 + 186 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 187 + if (!ctx) 188 + return -ENOMEM; 189 + 190 + ctx->aon_node = parent_dev->of_node; 191 + 192 + ctx->clkgen_reset = 193 + devm_reset_control_get_exclusive(parent_dev, "gpu-clkgen"); 194 + if (IS_ERR(ctx->clkgen_reset)) 195 + return dev_err_probe( 196 + dev, PTR_ERR(ctx->clkgen_reset), 197 + "Failed to get GPU clkgen reset from parent\n"); 198 + 199 + config.parent = dev; 200 + config.owner = THIS_MODULE; 201 + config.drvdata = ctx; 202 + config.match = pwrseq_thead_gpu_match; 203 + config.targets = pwrseq_thead_gpu_targets; 204 + 205 + ctx->pwrseq = devm_pwrseq_device_register(dev, &config); 206 + if (IS_ERR(ctx->pwrseq)) 207 + return dev_err_probe(dev, PTR_ERR(ctx->pwrseq), 208 + "Failed to register power sequencer\n"); 209 + 210 + auxiliary_set_drvdata(adev, ctx); 211 + 212 + return 0; 213 + } 214 + 215 + static void pwrseq_thead_gpu_remove(struct auxiliary_device *adev) 216 + { 217 + struct pwrseq_thead_gpu_ctx *ctx = auxiliary_get_drvdata(adev); 218 + 219 + if (ctx->gpu_reset) 220 + reset_control_put(ctx->gpu_reset); 221 + 222 + if (ctx->clks) { 223 + clk_bulk_put(ctx->num_clks, ctx->clks); 224 + kfree(ctx->clks); 225 + } 226 + 227 + if (ctx->consumer_node) 228 + of_node_put(ctx->consumer_node); 229 + } 230 + 231 + static const struct auxiliary_device_id pwrseq_thead_gpu_id_table[] = { 232 + { .name = "th1520_pm_domains.pwrseq-gpu" }, 233 + {}, 234 + }; 235 + MODULE_DEVICE_TABLE(auxiliary, pwrseq_thead_gpu_id_table); 236 + 237 + static struct auxiliary_driver pwrseq_thead_gpu_driver = { 238 + .driver = { 239 + .name = "pwrseq-thead-gpu", 240 + }, 241 + .probe = pwrseq_thead_gpu_probe, 242 + .remove = pwrseq_thead_gpu_remove, 243 + .id_table = pwrseq_thead_gpu_id_table, 244 + }; 245 + module_auxiliary_driver(pwrseq_thead_gpu_driver); 246 + 247 + MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>"); 248 + MODULE_DESCRIPTION("T-HEAD TH1520 GPU power sequencer driver"); 249 + MODULE_LICENSE("GPL");
+3
include/linux/pwrseq/provider.h
··· 13 13 typedef int (*pwrseq_power_state_func)(struct pwrseq_device *); 14 14 typedef int (*pwrseq_match_func)(struct pwrseq_device *, struct device *); 15 15 16 + #define PWRSEQ_NO_MATCH 0 17 + #define PWRSEQ_MATCH_OK 1 18 + 16 19 /** 17 20 * struct pwrseq_unit_data - Configuration of a single power sequencing 18 21 * unit.