Skip to content

Commit ffec878

Browse files
committed
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
2 parents fcb117e + 07d59de commit ffec878

File tree

7 files changed

+271
-9
lines changed

7 files changed

+271
-9
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21434,6 +21434,7 @@ F: drivers/mailbox/mailbox-th1520.c
2143421434
F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
2143521435
F: drivers/pinctrl/pinctrl-th1520.c
2143621436
F: drivers/pmdomain/thead/
21437+
F: drivers/power/sequencing/pwrseq-thead-gpu.c
2143721438
F: drivers/reset/reset-th1520.c
2143821439
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
2143921440
F: include/dt-bindings/power/thead,th1520-power.h

drivers/power/sequencing/Kconfig

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ if POWER_SEQUENCING
1616
config POWER_SEQUENCING_QCOM_WCN
1717
tristate "Qualcomm WCN family PMU driver"
1818
default m if ARCH_QCOM
19-
depends on OF
19+
depends on OF || COMPILE_TEST
2020
help
2121
Say Y here to enable the power sequencing driver for Qualcomm
2222
WCN Bluetooth/WLAN chipsets.
@@ -27,4 +27,12 @@ config POWER_SEQUENCING_QCOM_WCN
2727
this driver is needed for correct power control or else we'd risk not
2828
respecting the required delays between enabling Bluetooth and WLAN.
2929

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.
37+
3038
endif

drivers/power/sequencing/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ obj-$(CONFIG_POWER_SEQUENCING) += pwrseq-core.o
44
pwrseq-core-y := core.o
55

66
obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o
7+
obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o

drivers/power/sequencing/core.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
628628
return 0;
629629

630630
ret = pwrseq->match(pwrseq, match_data->dev);
631-
if (ret <= 0)
631+
if (ret == PWRSEQ_NO_MATCH || ret < 0)
632632
return ret;
633633

634634
/* We got the matching device, let's find the right target. */
@@ -651,7 +651,7 @@ static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
651651

652652
match_data->desc->pwrseq = pwrseq_device_get(pwrseq);
653653

654-
return 1;
654+
return PWRSEQ_MATCH_OK;
655655
}
656656

657657
/**
@@ -684,7 +684,7 @@ struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target)
684684
pwrseq_match_device);
685685
if (ret < 0)
686686
return ERR_PTR(ret);
687-
if (ret == 0)
687+
if (ret == PWRSEQ_NO_MATCH)
688688
/* No device matched. */
689689
return ERR_PTR(-EPROBE_DEFER);
690690

drivers/power/sequencing/pwrseq-qcom-wcn.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = {
155155
};
156156

157157
static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = {
158-
.name = "wlan-enable",
158+
.name = "bluetooth-enable",
159159
.deps = pwrseq_qcom_wcn6855_unit_deps,
160160
.enable = pwrseq_qcom_wcn_bt_enable,
161161
.disable = pwrseq_qcom_wcn_bt_disable,
@@ -341,12 +341,12 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
341341
* device.
342342
*/
343343
if (!of_property_present(dev_node, "vddaon-supply"))
344-
return 0;
344+
return PWRSEQ_NO_MATCH;
345345

346346
struct device_node *reg_node __free(device_node) =
347347
of_parse_phandle(dev_node, "vddaon-supply", 0);
348348
if (!reg_node)
349-
return 0;
349+
return PWRSEQ_NO_MATCH;
350350

351351
/*
352352
* `reg_node` is the PMU AON regulator, its parent is the `regulators`
@@ -355,9 +355,9 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
355355
*/
356356
if (!reg_node->parent || !reg_node->parent->parent ||
357357
reg_node->parent->parent != ctx->of_node)
358-
return 0;
358+
return PWRSEQ_NO_MATCH;
359359

360-
return 1;
360+
return PWRSEQ_MATCH_OK;
361361
}
362362

363363
static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
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 <[email protected]>
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 <[email protected]>");
248+
MODULE_DESCRIPTION("T-HEAD TH1520 GPU power sequencer driver");
249+
MODULE_LICENSE("GPL");

include/linux/pwrseq/provider.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ struct pwrseq_device;
1313
typedef int (*pwrseq_power_state_func)(struct pwrseq_device *);
1414
typedef int (*pwrseq_match_func)(struct pwrseq_device *, struct device *);
1515

16+
#define PWRSEQ_NO_MATCH 0
17+
#define PWRSEQ_MATCH_OK 1
18+
1619
/**
1720
* struct pwrseq_unit_data - Configuration of a single power sequencing
1821
* unit.

0 commit comments

Comments
 (0)