power sequencing updates for v6.17-rc1
New drivers:
- 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
Fixes:
- Fix the name of the bluetooth-enable unit for WCN6855
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmiHJuUACgkQEacuoBRx
13JZWw/+NTpTLn1hDcn6dXIL3A88qE6/pv2VyoFOC2vCmwUFivcNH1aC1IhlxxEk
paEePhhRJnbinxruAlq4/Plx0i6E54N6pu6L3RazlJPThHVe9GIB1cdIYB4EtWjf
AOL+LIKGTrwC3mdQL8QgdLVjoefW71KyX2vKkWhNQ6azJBcDvfbc/oolT99VgUCx
j4XyXH1lfcA4LCytY63Ol4LxhSX2SRjT1TvPa9U673+K+Hs09oh21C7LAeOuVVTi
PwEvwXw0gTTvns7xJgIMXqmDeQ+Pb9RzQ7c1yW9jthr/RoaWXedQoPkcWSTi+1H6
DWIJpAl7qMRQj4nVk0+gAk9a+OUwoi4cs0Efjh4V7Pa1G8bMOSdzhySn2qUKu5n+
Wb5u5R/G9viIP06Qan4WOiVDzs8idQRg3iImwZmXul0G3wKmrFVIGtor8LF4ey0w
ggqVuOb8XVG7U2tMgVPcnbI2LG8rosw+a9Y4Bio69/iNDI4yob3LiOIFd/m+uX65
G64F8/ATxkXsO3WJKE752kz1y8GsfHFHE+37VM36dUU8sInouiwOTOoCKc3q0Lq7
91w0sNRcOQ+YrwSHezQEO3fm4fZGMLzjR7Q2fZnrOuwf36MxREqQxTN3FnyBOC2x
qASlNvGqPa3L/944N8N/F0fcHdSqTI17vQDVoCi7Hyz+NdWTIdE=
=qc2Z
-----END PGP SIGNATURE-----
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
pull/1309/head
commit
ffec878fa5
|
|
@ -21434,6 +21434,7 @@ F: drivers/mailbox/mailbox-th1520.c
|
|||
F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
|
||||
F: drivers/pinctrl/pinctrl-th1520.c
|
||||
F: drivers/pmdomain/thead/
|
||||
F: drivers/power/sequencing/pwrseq-thead-gpu.c
|
||||
F: drivers/reset/reset-th1520.c
|
||||
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
|
||||
F: include/dt-bindings/power/thead,th1520-power.h
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ if POWER_SEQUENCING
|
|||
config POWER_SEQUENCING_QCOM_WCN
|
||||
tristate "Qualcomm WCN family PMU driver"
|
||||
default m if ARCH_QCOM
|
||||
depends on OF
|
||||
depends on OF || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable the power sequencing driver for Qualcomm
|
||||
WCN Bluetooth/WLAN chipsets.
|
||||
|
|
@ -27,4 +27,12 @@ config POWER_SEQUENCING_QCOM_WCN
|
|||
this driver is needed for correct power control or else we'd risk not
|
||||
respecting the required delays between enabling Bluetooth and WLAN.
|
||||
|
||||
config POWER_SEQUENCING_TH1520_GPU
|
||||
tristate "T-HEAD TH1520 GPU power sequencing driver"
|
||||
depends on (ARCH_THEAD && AUXILIARY_BUS) || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable the power sequencing driver for the TH1520 SoC
|
||||
GPU. This driver handles the complex clock and reset sequence
|
||||
required to power on the Imagination BXM GPU on this platform.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ obj-$(CONFIG_POWER_SEQUENCING) += pwrseq-core.o
|
|||
pwrseq-core-y := core.o
|
||||
|
||||
obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o
|
||||
obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ static int pwrseq_match_device(struct device *pwrseq_dev, void *data)
|
|||
return 0;
|
||||
|
||||
ret = pwrseq->match(pwrseq, match_data->dev);
|
||||
if (ret <= 0)
|
||||
if (ret == PWRSEQ_NO_MATCH || ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 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)
|
|||
|
||||
match_data->desc->pwrseq = pwrseq_device_get(pwrseq);
|
||||
|
||||
return 1;
|
||||
return PWRSEQ_MATCH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -684,7 +684,7 @@ struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target)
|
|||
pwrseq_match_device);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
if (ret == 0)
|
||||
if (ret == PWRSEQ_NO_MATCH)
|
||||
/* No device matched. */
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = {
|
|||
};
|
||||
|
||||
static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = {
|
||||
.name = "wlan-enable",
|
||||
.name = "bluetooth-enable",
|
||||
.deps = pwrseq_qcom_wcn6855_unit_deps,
|
||||
.enable = pwrseq_qcom_wcn_bt_enable,
|
||||
.disable = pwrseq_qcom_wcn_bt_disable,
|
||||
|
|
@ -341,12 +341,12 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
|
|||
* device.
|
||||
*/
|
||||
if (!of_property_present(dev_node, "vddaon-supply"))
|
||||
return 0;
|
||||
return PWRSEQ_NO_MATCH;
|
||||
|
||||
struct device_node *reg_node __free(device_node) =
|
||||
of_parse_phandle(dev_node, "vddaon-supply", 0);
|
||||
if (!reg_node)
|
||||
return 0;
|
||||
return PWRSEQ_NO_MATCH;
|
||||
|
||||
/*
|
||||
* `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,
|
|||
*/
|
||||
if (!reg_node->parent || !reg_node->parent->parent ||
|
||||
reg_node->parent->parent != ctx->of_node)
|
||||
return 0;
|
||||
return PWRSEQ_NO_MATCH;
|
||||
|
||||
return 1;
|
||||
return PWRSEQ_MATCH_OK;
|
||||
}
|
||||
|
||||
static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* T-HEAD TH1520 GPU Power Sequencer Driver
|
||||
*
|
||||
* Copyright (c) 2025 Samsung Electronics Co., Ltd.
|
||||
* Author: Michal Wilczynski <m.wilczynski@samsung.com>
|
||||
*
|
||||
* This driver implements the power sequence for the Imagination BXM-4-64
|
||||
* GPU on the T-HEAD TH1520 SoC. The sequence requires coordinating resources
|
||||
* from both the sequencer's parent device node (clkgen_reset) and the GPU's
|
||||
* device node (clocks and core reset).
|
||||
*
|
||||
* The `match` function is used to acquire the GPU's resources when the
|
||||
* GPU driver requests the "gpu-power" sequence target.
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pwrseq/provider.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/power/thead,th1520-power.h>
|
||||
|
||||
struct pwrseq_thead_gpu_ctx {
|
||||
struct pwrseq_device *pwrseq;
|
||||
struct reset_control *clkgen_reset;
|
||||
struct device_node *aon_node;
|
||||
|
||||
/* Consumer resources */
|
||||
struct device_node *consumer_node;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
struct reset_control *gpu_reset;
|
||||
};
|
||||
|
||||
static int pwrseq_thead_gpu_enable(struct pwrseq_device *pwrseq)
|
||||
{
|
||||
struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
int ret;
|
||||
|
||||
if (!ctx->clks || !ctx->gpu_reset)
|
||||
return -ENODEV;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ctx->num_clks, ctx->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(ctx->clkgen_reset);
|
||||
if (ret)
|
||||
goto err_disable_clks;
|
||||
|
||||
/*
|
||||
* According to the hardware manual, a delay of at least 32 clock
|
||||
* cycles is required between de-asserting the clkgen reset and
|
||||
* de-asserting the GPU reset. Assuming a worst-case scenario with
|
||||
* a very high GPU clock frequency, a delay of 1 microsecond is
|
||||
* sufficient to ensure this requirement is met across all
|
||||
* feasible GPU clock speeds.
|
||||
*/
|
||||
udelay(1);
|
||||
|
||||
ret = reset_control_deassert(ctx->gpu_reset);
|
||||
if (ret)
|
||||
goto err_assert_clkgen;
|
||||
|
||||
return 0;
|
||||
|
||||
err_assert_clkgen:
|
||||
reset_control_assert(ctx->clkgen_reset);
|
||||
err_disable_clks:
|
||||
clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pwrseq_thead_gpu_disable(struct pwrseq_device *pwrseq)
|
||||
{
|
||||
struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
int ret = 0, err;
|
||||
|
||||
if (!ctx->clks || !ctx->gpu_reset)
|
||||
return -ENODEV;
|
||||
|
||||
err = reset_control_assert(ctx->gpu_reset);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = reset_control_assert(ctx->clkgen_reset);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
|
||||
clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks);
|
||||
|
||||
/* ret stores values of the first error code */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pwrseq_unit_data pwrseq_thead_gpu_unit = {
|
||||
.name = "gpu-power-sequence",
|
||||
.enable = pwrseq_thead_gpu_enable,
|
||||
.disable = pwrseq_thead_gpu_disable,
|
||||
};
|
||||
|
||||
static const struct pwrseq_target_data pwrseq_thead_gpu_target = {
|
||||
.name = "gpu-power",
|
||||
.unit = &pwrseq_thead_gpu_unit,
|
||||
};
|
||||
|
||||
static const struct pwrseq_target_data *pwrseq_thead_gpu_targets[] = {
|
||||
&pwrseq_thead_gpu_target,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int pwrseq_thead_gpu_match(struct pwrseq_device *pwrseq,
|
||||
struct device *dev)
|
||||
{
|
||||
struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
static const char *const clk_names[] = { "core", "sys" };
|
||||
struct of_phandle_args pwr_spec;
|
||||
int i, ret;
|
||||
|
||||
/* We only match the specific T-HEAD TH1520 GPU compatible */
|
||||
if (!of_device_is_compatible(dev->of_node, "thead,th1520-gpu"))
|
||||
return PWRSEQ_NO_MATCH;
|
||||
|
||||
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
|
||||
"#power-domain-cells", 0, &pwr_spec);
|
||||
if (ret)
|
||||
return PWRSEQ_NO_MATCH;
|
||||
|
||||
/* Additionally verify consumer device has AON as power-domain */
|
||||
if (pwr_spec.np != ctx->aon_node || pwr_spec.args[0] != TH1520_GPU_PD) {
|
||||
of_node_put(pwr_spec.np);
|
||||
return PWRSEQ_NO_MATCH;
|
||||
}
|
||||
|
||||
of_node_put(pwr_spec.np);
|
||||
|
||||
/* If a consumer is already bound, only allow a re-match from it */
|
||||
if (ctx->consumer_node)
|
||||
return ctx->consumer_node == dev->of_node ?
|
||||
PWRSEQ_MATCH_OK : PWRSEQ_NO_MATCH;
|
||||
|
||||
ctx->num_clks = ARRAY_SIZE(clk_names);
|
||||
ctx->clks = kcalloc(ctx->num_clks, sizeof(*ctx->clks), GFP_KERNEL);
|
||||
if (!ctx->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ctx->num_clks; i++)
|
||||
ctx->clks[i].id = clk_names[i];
|
||||
|
||||
ret = clk_bulk_get(dev, ctx->num_clks, ctx->clks);
|
||||
if (ret)
|
||||
goto err_free_clks;
|
||||
|
||||
ctx->gpu_reset = reset_control_get_shared(dev, NULL);
|
||||
if (IS_ERR(ctx->gpu_reset)) {
|
||||
ret = PTR_ERR(ctx->gpu_reset);
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
ctx->consumer_node = of_node_get(dev->of_node);
|
||||
|
||||
return PWRSEQ_MATCH_OK;
|
||||
|
||||
err_put_clks:
|
||||
clk_bulk_put(ctx->num_clks, ctx->clks);
|
||||
err_free_clks:
|
||||
kfree(ctx->clks);
|
||||
ctx->clks = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pwrseq_thead_gpu_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct device *parent_dev = dev->parent;
|
||||
struct pwrseq_thead_gpu_ctx *ctx;
|
||||
struct pwrseq_config config = {};
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->aon_node = parent_dev->of_node;
|
||||
|
||||
ctx->clkgen_reset =
|
||||
devm_reset_control_get_exclusive(parent_dev, "gpu-clkgen");
|
||||
if (IS_ERR(ctx->clkgen_reset))
|
||||
return dev_err_probe(
|
||||
dev, PTR_ERR(ctx->clkgen_reset),
|
||||
"Failed to get GPU clkgen reset from parent\n");
|
||||
|
||||
config.parent = dev;
|
||||
config.owner = THIS_MODULE;
|
||||
config.drvdata = ctx;
|
||||
config.match = pwrseq_thead_gpu_match;
|
||||
config.targets = pwrseq_thead_gpu_targets;
|
||||
|
||||
ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
|
||||
if (IS_ERR(ctx->pwrseq))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
|
||||
"Failed to register power sequencer\n");
|
||||
|
||||
auxiliary_set_drvdata(adev, ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pwrseq_thead_gpu_remove(struct auxiliary_device *adev)
|
||||
{
|
||||
struct pwrseq_thead_gpu_ctx *ctx = auxiliary_get_drvdata(adev);
|
||||
|
||||
if (ctx->gpu_reset)
|
||||
reset_control_put(ctx->gpu_reset);
|
||||
|
||||
if (ctx->clks) {
|
||||
clk_bulk_put(ctx->num_clks, ctx->clks);
|
||||
kfree(ctx->clks);
|
||||
}
|
||||
|
||||
if (ctx->consumer_node)
|
||||
of_node_put(ctx->consumer_node);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id pwrseq_thead_gpu_id_table[] = {
|
||||
{ .name = "th1520_pm_domains.pwrseq-gpu" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, pwrseq_thead_gpu_id_table);
|
||||
|
||||
static struct auxiliary_driver pwrseq_thead_gpu_driver = {
|
||||
.driver = {
|
||||
.name = "pwrseq-thead-gpu",
|
||||
},
|
||||
.probe = pwrseq_thead_gpu_probe,
|
||||
.remove = pwrseq_thead_gpu_remove,
|
||||
.id_table = pwrseq_thead_gpu_id_table,
|
||||
};
|
||||
module_auxiliary_driver(pwrseq_thead_gpu_driver);
|
||||
|
||||
MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
|
||||
MODULE_DESCRIPTION("T-HEAD TH1520 GPU power sequencer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -13,6 +13,9 @@ struct pwrseq_device;
|
|||
typedef int (*pwrseq_power_state_func)(struct pwrseq_device *);
|
||||
typedef int (*pwrseq_match_func)(struct pwrseq_device *, struct device *);
|
||||
|
||||
#define PWRSEQ_NO_MATCH 0
|
||||
#define PWRSEQ_MATCH_OK 1
|
||||
|
||||
/**
|
||||
* struct pwrseq_unit_data - Configuration of a single power sequencing
|
||||
* unit.
|
||||
|
|
|
|||
Loading…
Reference in New Issue