While we are waiting for all users of wcn399x-bt to be converted to the pwrseq declaration in the device tree, provide support for the pwrseq fallback: if the regulators are declared in the device itself, create pwrseq instance. This way the hci_qca driver doesn't have to cope with old and new dts bindings. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> --- drivers/power/pwrseq/pwrseq_qca.c | 155 +++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c index c15508cc80d2..f237cf2f1880 100644 --- a/drivers/power/pwrseq/pwrseq_qca.c +++ b/drivers/power/pwrseq/pwrseq_qca.c @@ -11,9 +11,11 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/mod_devicetable.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/pwrseq/driver.h> +#include <linux/pwrseq/fallback.h> #include <linux/regulator/consumer.h> /* susclk rate */ @@ -369,5 +371,156 @@ static struct platform_driver pwrseq_qca_driver = { }, }; -module_platform_driver(pwrseq_qca_driver); +struct pwrseq_qca_fallback { + struct pwrseq_qca_one qca_one; + struct pwrseq_qca_common common; +}; + +static const struct of_device_id pwrseq_qca_bt_of_match[] = { + { .compatible = "qcom,qca6174-bt" }, + { .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390}, + { .compatible = "qcom,qca9377-bt" }, + { .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990 }, + { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3990 }, + { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998 }, + { .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750 }, + { /* sentinel */ }, +}; + +static const struct qca_device_data qca_soc_data_wifi = { + .vregs = { + { "vdd-1.8-xo", 80000 }, + { "vdd-1.3-rfa", 300000 }, + { "vdd-3.3-ch0", 450000 }, + { "vdd-3.3-ch1", 450000 }, + }, + .num_vregs = 4, +}; + +static const struct of_device_id pwrseq_qca_wifi_of_match[] = { + { .compatible = "qcom,wcn3990-wifi", .data = &qca_soc_data_wifi }, + { /* sentinel */ } +}; + +static struct pwrseq * pwrseq_qca_fallback_get(struct device *dev) +{ + struct pwrseq_qca_fallback *fallback; + struct pwrseq *pwrseq; + const struct of_device_id *match; + const struct qca_device_data *data; + struct gpio_desc *gpiod; + int ret; + + match = of_match_device(pwrseq_qca_bt_of_match, dev); + if (!match) + return ERR_PTR(-ENODEV); + + data = match->data; + if (!data) + data = &qca_soc_data_default; + + fallback = devm_kzalloc(dev, struct_size(fallback, common.vregs, data->num_vregs), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + fallback->qca_one.common = &fallback->common; + + ret = pwrseq_qca_common_init(dev, &fallback->common, data); + if (ret) + return ERR_PTR(ret); + + if (data->has_enable_gpios) { + gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + return ERR_PTR(dev_err_probe(dev, PTR_ERR(gpiod), "failed to acquire enable GPIO\n")); + fallback->qca_one.enable = gpiod; + } + + /* If we have no control over device's enablement, make sure that sleep clock is always running */ + if (!fallback->common.vddio || + !fallback->common.num_vregs || + !fallback->qca_one.enable) { + ret = clk_set_rate(fallback->common.susclk, SUSCLK_RATE_32KHZ); + if (ret) + return ERR_PTR(ret); + + ret = clk_prepare_enable(fallback->common.susclk); + if (ret) + return ERR_PTR(ret); + + ret = devm_add_action_or_reset(dev, pwrseq_qca_unprepare_susclk, &fallback->common); + if (ret) + return ERR_PTR(ret); + } + + pwrseq = devm_pwrseq_create(dev, &pwrseq_qca_ops); + if (IS_ERR(pwrseq)) + return pwrseq; + + pwrseq_set_drvdata(pwrseq, &fallback->qca_one); + + return pwrseq; +} + +static struct pwrseq * pwrseq_qca_fallback_get_bt(struct device *dev, const char *id) +{ + if (strcmp(id, "bt")) + return ERR_PTR(-ENODEV); + + return pwrseq_qca_fallback_get(dev); +} + +static struct pwrseq * pwrseq_qca_fallback_get_wifi(struct device *dev, const char *id) +{ + if (strcmp(id, "wifi")) + return ERR_PTR(-ENODEV); + + return pwrseq_qca_fallback_get(dev); +} + +static struct pwrseq_fallback pwrseq_qca_fallback_bt = { + .get = pwrseq_qca_fallback_get_bt, + .of_match_table = pwrseq_qca_bt_of_match, +}; + +static struct pwrseq_fallback pwrseq_qca_fallback_wifi = { + .get = pwrseq_qca_fallback_get_wifi, + .of_match_table = pwrseq_qca_wifi_of_match, +}; + +static int __init pwrseq_qca_init(void) +{ + int ret; + + ret = platform_driver_register(&pwrseq_qca_driver); + if (ret) + return ret; + + ret = pwrseq_fallback_register(&pwrseq_qca_fallback_bt); + if (ret) + goto err_bt; + + ret = pwrseq_fallback_register(&pwrseq_qca_fallback_wifi); + if (ret) + goto err_wifi; + + return 0; + +err_wifi: + pwrseq_fallback_unregister(&pwrseq_qca_fallback_bt); +err_bt: + platform_driver_unregister(&pwrseq_qca_driver); + + return ret; +} +module_init(pwrseq_qca_init); + +static void __exit pwrseq_qca_exit(void) +{ + pwrseq_fallback_unregister(&pwrseq_qca_fallback_wifi); + pwrseq_fallback_unregister(&pwrseq_qca_fallback_bt); + platform_driver_unregister(&pwrseq_qca_driver); +} +module_exit(pwrseq_qca_exit); + MODULE_LICENSE("GPL v2"); -- 2.33.0