From f82bf3c5796e1630d553669fb451e6c9d4070512 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Oct 2024 15:04:49 +0200 Subject: [PATCH 1/2] power: sequencing: make the QCom PMU pwrseq driver depend on CONFIG_OF This driver uses various OF-specific functions and depends on phandle parsing. There's no reason to make it available to non-OF systems so add a relevant dependency switch to its Kconfig entry. Fixes: 2f1630f437df ("power: pwrseq: add a driver for the PMU module on the QCom WCN chipsets") Link: https://lore.kernel.org/r/20241004130449.51725-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig index c9f1cdb66524..ddcc42a98492 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -16,6 +16,7 @@ if POWER_SEQUENCING config POWER_SEQUENCING_QCOM_WCN tristate "Qualcomm WCN family PMU driver" default m if ARCH_QCOM + depends on OF help Say Y here to enable the power sequencing driver for Qualcomm WCN Bluetooth/WLAN chipsets. From bd4c8bafcf50b6bd415c8bf04e98ebfba78071f9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 18 Oct 2024 14:49:12 +0200 Subject: [PATCH 2/2] power: sequencing: qcom-wcn: improve support for wcn6855 WCN6855 (also known as QCA6490) is similar to the already supported QCA6390 but takes in two more supplies so add a new vregs list for it. On sm8450-hdk it also requires a short assert of the xo-clk pin so add handling for it in a dedicated unit. As we now have a separate set of targets for this variant, store the pointer to the targets struct associated with a model in the device match data. Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20241018-sc8280xp-pwrseq-v6-2-8da8310d9564@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-qcom-wcn.c | 101 ++++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-qcom-wcn.c b/drivers/power/sequencing/pwrseq-qcom-wcn.c index 4fa129877d7e..682a9beac69e 100644 --- a/drivers/power/sequencing/pwrseq-qcom-wcn.c +++ b/drivers/power/sequencing/pwrseq-qcom-wcn.c @@ -22,6 +22,7 @@ struct pwrseq_qcom_wcn_pdata { size_t num_vregs; unsigned int pwup_delay_ms; unsigned int gpio_enable_delay_ms; + const struct pwrseq_target_data **targets; }; struct pwrseq_qcom_wcn_ctx { @@ -31,6 +32,7 @@ struct pwrseq_qcom_wcn_ctx { struct regulator_bulk_data *regs; struct gpio_desc *bt_gpio; struct gpio_desc *wlan_gpio; + struct gpio_desc *xo_clk_gpio; struct clk *clk; unsigned long last_gpio_enable_jf; }; @@ -98,6 +100,33 @@ static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = { NULL }; +static int pwrseq_qcom_wcn6855_clk_assert(struct pwrseq_device *pwrseq) +{ + struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + if (!ctx->xo_clk_gpio) + return 0; + + msleep(1); + + gpiod_set_value_cansleep(ctx->xo_clk_gpio, 1); + usleep_range(100, 200); + + return 0; +} + +static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_xo_clk_assert = { + .name = "xo-clk-assert", + .enable = pwrseq_qcom_wcn6855_clk_assert, +}; + +static const struct pwrseq_unit_data *pwrseq_qcom_wcn6855_unit_deps[] = { + &pwrseq_qcom_wcn_vregs_unit_data, + &pwrseq_qcom_wcn_clk_unit_data, + &pwrseq_qcom_wcn6855_xo_clk_assert, + NULL +}; + static int pwrseq_qcom_wcn_bt_enable(struct pwrseq_device *pwrseq) { struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); @@ -125,6 +154,13 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = { .disable = pwrseq_qcom_wcn_bt_disable, }; +static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = { + .name = "wlan-enable", + .deps = pwrseq_qcom_wcn6855_unit_deps, + .enable = pwrseq_qcom_wcn_bt_enable, + .disable = pwrseq_qcom_wcn_bt_disable, +}; + static int pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device *pwrseq) { struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); @@ -152,6 +188,13 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_wlan_unit_data = { .disable = pwrseq_qcom_wcn_wlan_disable, }; +static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_wlan_unit_data = { + .name = "wlan-enable", + .deps = pwrseq_qcom_wcn6855_unit_deps, + .enable = pwrseq_qcom_wcn_wlan_enable, + .disable = pwrseq_qcom_wcn_wlan_disable, +}; + static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq) { struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); @@ -162,6 +205,18 @@ static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq) return 0; } +static int pwrseq_qcom_wcn6855_xo_clk_deassert(struct pwrseq_device *pwrseq) +{ + struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + + if (ctx->xo_clk_gpio) { + usleep_range(2000, 5000); + gpiod_set_value_cansleep(ctx->xo_clk_gpio, 0); + } + + return pwrseq_qcom_wcn_pwup_delay(pwrseq); +} + static const struct pwrseq_target_data pwrseq_qcom_wcn_bt_target_data = { .name = "bluetooth", .unit = &pwrseq_qcom_wcn_bt_unit_data, @@ -174,12 +229,30 @@ static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = { .post_enable = pwrseq_qcom_wcn_pwup_delay, }; +static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = { + .name = "bluetooth", + .unit = &pwrseq_qcom_wcn6855_bt_unit_data, + .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert, +}; + +static const struct pwrseq_target_data pwrseq_qcom_wcn6855_wlan_target_data = { + .name = "wlan", + .unit = &pwrseq_qcom_wcn6855_wlan_unit_data, + .post_enable = pwrseq_qcom_wcn6855_xo_clk_deassert, +}; + static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = { &pwrseq_qcom_wcn_bt_target_data, &pwrseq_qcom_wcn_wlan_target_data, NULL }; +static const struct pwrseq_target_data *pwrseq_qcom_wcn6855_targets[] = { + &pwrseq_qcom_wcn6855_bt_target_data, + &pwrseq_qcom_wcn6855_wlan_target_data, + NULL +}; + static const char *const pwrseq_qca6390_vregs[] = { "vddio", "vddaon", @@ -196,13 +269,28 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = { .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs), .pwup_delay_ms = 60, .gpio_enable_delay_ms = 100, + .targets = pwrseq_qcom_wcn_targets, +}; + +static const char *const pwrseq_wcn6855_vregs[] = { + "vddio", + "vddaon", + "vddpmu", + "vddpmumx", + "vddpmucx", + "vddrfa0p95", + "vddrfa1p3", + "vddrfa1p9", + "vddpcie1p3", + "vddpcie1p9", }; static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn6855_of_data = { - .vregs = pwrseq_qca6390_vregs, - .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs), + .vregs = pwrseq_wcn6855_vregs, + .num_vregs = ARRAY_SIZE(pwrseq_wcn6855_vregs), .pwup_delay_ms = 50, .gpio_enable_delay_ms = 5, + .targets = pwrseq_qcom_wcn6855_targets, }; static const char *const pwrseq_wcn7850_vregs[] = { @@ -219,6 +307,7 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = { .vregs = pwrseq_wcn7850_vregs, .num_vregs = ARRAY_SIZE(pwrseq_wcn7850_vregs), .pwup_delay_ms = 50, + .targets = pwrseq_qcom_wcn_targets, }; static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, @@ -295,6 +384,12 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio), "Failed to get the WLAN enable GPIO\n"); + ctx->xo_clk_gpio = devm_gpiod_get_optional(dev, "xo-clk", + GPIOD_OUT_LOW); + if (IS_ERR(ctx->xo_clk_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->xo_clk_gpio), + "Failed to get the XO_CLK GPIO\n"); + /* * Set direction to output but keep the current value in order to not * disable the WLAN module accidentally if it's already powered on. @@ -313,7 +408,7 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) config.owner = THIS_MODULE; config.drvdata = ctx; config.match = pwrseq_qcom_wcn_match; - config.targets = pwrseq_qcom_wcn_targets; + config.targets = ctx->pdata->targets; ctx->pwrseq = devm_pwrseq_device_register(dev, &config); if (IS_ERR(ctx->pwrseq))