aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-yocto/qca6390-driver
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/linux-yocto/qca6390-driver')
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0001-dt-bindings-mfd-qcom-qca639x-add-binding-for-QCA639x.patch111
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch225
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0003-mfd-qcom-qca639x-switch-to-platform-config-data.patch189
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0004-mfd-qcom-qca639x-change-qca639x-to-use-gpios-rather-.patch90
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0005-mfd-qcom-qca639x-Add-support-for-WCN6855.patch111
5 files changed, 726 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-yocto/qca6390-driver/0001-dt-bindings-mfd-qcom-qca639x-add-binding-for-QCA639x.patch b/recipes-kernel/linux/linux-yocto/qca6390-driver/0001-dt-bindings-mfd-qcom-qca639x-add-binding-for-QCA639x.patch
new file mode 100644
index 0000000..a9881f0
--- /dev/null
+++ b/recipes-kernel/linux/linux-yocto/qca6390-driver/0001-dt-bindings-mfd-qcom-qca639x-add-binding-for-QCA639x.patch
@@ -0,0 +1,111 @@
+From 3b909d078f454238bb9e8ec454a891765df968f6 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Sun, 20 Dec 2020 18:47:57 +0300
+Subject: [PATCH 1/5] dt-bindings: mfd: qcom,qca639x: add binding for QCA639x
+ defvice
+
+Qualcomm QCA639x is a family of WiFi + Bluetooth SoCs, with BT part
+being controlled through the UART and WiFi being present on PCIe bus.
+Both blocks share common power sources. Add binding to describe power
+sequencing required to power up this device.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Upstream-Status: Inappropriate
+---
+ .../devicetree/bindings/mfd/qcom,qca639x.yaml | 84 +++++++++++++++++++
+ 1 file changed, 84 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml
+
+diff --git a/Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml b/Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml
+new file mode 100644
+index 000000000000..d43c75da136f
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml
+@@ -0,0 +1,84 @@
++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: "http://devicetree.org/schemas/mfd/qcom,qca639x.yaml#"
++$schema: "http://devicetree.org/meta-schemas/core.yaml#"
++
++title: Qualcomm QCA639x WiFi + Bluetoot SoC bindings
++
++maintainers:
++ - Andy Gross <agross@kernel.org>
++ - Bjorn Andersson <bjorn.andersson@linaro.org>
++
++description: |
++ This binding describes thes Qualcomm QCA6390 or QCA6391 power supplies and
++ enablement pins.
++
++properties:
++ compatible:
++ const: qcom,qca639x
++
++ '#power-domain-cells':
++ const: 0
++
++ pinctrl-0: true
++ pinctrl-1: true
++
++ pinctrl-names:
++ items:
++ - const: default
++ - const: active
++
++ vddaon-supply:
++ description:
++ 0.95V always-on LDO power input
++
++ vddpmu-supply:
++ description:
++ 0.95V LDO power input to PMU
++
++ vddrfa1-supply:
++ description:
++ 0.95V LDO power input to RFA
++
++ vddrfa2-supply:
++ description:
++ 1.25V LDO power input to RFA
++
++ vddrfa3-supply:
++ description:
++ 2V LDO power input to RFA
++
++ vddpcie1-supply:
++ description:
++ 1.25V LDO power input to PCIe part
++
++ vddpcie2-supply:
++ description:
++ 2V LDO power input to PCIe part
++
++ vddio-supply:
++ description:
++ 1.8V VIO input
++
++additionalProperties: false
++
++examples:
++ - |
++ qca639x: qca639x {
++ compatible = "qcom,qca639x";
++ #power-domain-cells = <0>;
++
++ vddaon-supply = <&vreg_s6a_0p95>;
++ vddpmu-supply = <&vreg_s2f_0p95>;
++ vddrfa1-supply = <&vreg_s2f_0p95>;
++ vddrfa2-supply = <&vreg_s8c_1p3>;
++ vddrfa3-supply = <&vreg_s5a_1p9>;
++ vddpcie1-supply = <&vreg_s8c_1p3>;
++ vddpcie2-supply = <&vreg_s5a_1p9>;
++ vddio-supply = <&vreg_s4a_1p8>;
++ pinctrl-names = "default", "active";
++ pinctrl-0 = <&wlan_default_state &bt_default_state>;
++ pinctrl-1 = <&wlan_active_state &bt_active_state>;
++ };
++...
+--
+2.39.2
+
diff --git a/recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch b/recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch
new file mode 100644
index 0000000..2621853
--- /dev/null
+++ b/recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch
@@ -0,0 +1,225 @@
+From 6cca247e22ac57fcc99241fee201056c1967278e Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Fri, 18 Dec 2020 16:24:56 +0300
+Subject: [PATCH 2/5] mfd: qca639x: add support for QCA639x powerup sequence
+
+Qualcomm QCA639x is a family of WiFi + Bluetooth SoCs, with BT part
+being controlled through the UART and WiFi being present on PCIe
+bus. Both blocks share common power sources. So add mfd device driver
+handling power sequencing of QCA6390/1.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Upstream-Status: Inappropriate
+---
+ drivers/mfd/Kconfig | 12 +++
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/qcom-qca639x.c | 162 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 175 insertions(+)
+ create mode 100644 drivers/mfd/qcom-qca639x.c
+
+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
+index e90463c4441c..9abb4df8d66b 100644
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -1086,6 +1086,18 @@ config MFD_PM8XXX
+ Say M here if you want to include support for PM8xxx chips as a
+ module. This will build a module called "pm8xxx-core".
+
++config MFD_QCOM_QCA639X
++ tristate "Qualcomm QCA639x WiFi/Bluetooth module support"
++ depends on REGULATOR && PM_GENERIC_DOMAINS
++ help
++ If you say yes to this option, support will be included for Qualcomm
++ QCA639x family of WiFi and Bluetooth SoCs. Note, this driver supports
++ only power control for this SoC, you still have to enable individual
++ Bluetooth and WiFi drivers.
++
++ Say M here if you want to include support for QCA639x chips as a
++ module. This will build a module called "qcom-qca639x".
++
+ config MFD_QCOM_RPM
+ tristate "Qualcomm Resource Power Manager (RPM)"
+ depends on ARCH_QCOM && OF
+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
+index 1d2392f06f78..f7f25ef9e17a 100644
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -197,6 +197,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
+ obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
+ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
+ obj-$(CONFIG_MFD_PM8XXX) += qcom-pm8xxx.o ssbi.o
++obj-$(CONFIG_MFD_QCOM_QCA639X) += qcom-qca639x.o
+ obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o
+ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
+ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+diff --git a/drivers/mfd/qcom-qca639x.c b/drivers/mfd/qcom-qca639x.c
+new file mode 100644
+index 000000000000..b31e4b65bec5
+--- /dev/null
++++ b/drivers/mfd/qcom-qca639x.c
+@@ -0,0 +1,162 @@
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/pinctrl/devinfo.h>
++#include <linux/platform_device.h>
++#include <linux/pm_domain.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++
++#define MAX_NUM_REGULATORS 8
++
++static struct vreg {
++ const char *name;
++ unsigned int load_uA;
++} vregs [MAX_NUM_REGULATORS] = {
++ /* 2.0 V */
++ { "vddpcie2", 15000 },
++ { "vddrfa3", 400000 },
++
++ /* 0.95 V */
++ { "vddaon", 100000 },
++ { "vddpmu", 1250000 },
++ { "vddrfa1", 200000 },
++
++ /* 1.35 V */
++ { "vddrfa2", 400000 },
++ { "vddpcie1", 35000 },
++
++ /* 1.8 V */
++ { "vddio", 20000 },
++};
++
++struct qca639x_data {
++ struct regulator_bulk_data regulators[MAX_NUM_REGULATORS];
++ size_t num_vregs;
++ struct device *dev;
++ struct pinctrl_state *active_state;
++ struct generic_pm_domain pd;
++};
++
++#define domain_to_data(domain) container_of(domain, struct qca639x_data, pd)
++
++static int qca639x_power_on(struct generic_pm_domain *domain)
++{
++ struct qca639x_data *data = domain_to_data(domain);
++ int ret;
++
++ dev_warn(&domain->dev, "DUMMY POWER ON\n");
++
++ ret = regulator_bulk_enable(data->num_vregs, data->regulators);
++ if (ret) {
++ dev_err(data->dev, "Failed to enable regulators");
++ return ret;
++ }
++
++ /* Wait for 1ms before toggling enable pins. */
++ msleep(1);
++
++ ret = pinctrl_select_state(data->dev->pins->p, data->active_state);
++ if (ret) {
++ dev_err(data->dev, "Failed to select active state");
++ return ret;
++ }
++
++ /* Wait for all power levels to stabilize */
++ msleep(6);
++
++ return 0;
++}
++
++static int qca639x_power_off(struct generic_pm_domain *domain)
++{
++ struct qca639x_data *data = domain_to_data(domain);
++
++ dev_warn(&domain->dev, "DUMMY POWER OFF\n");
++
++ pinctrl_select_default_state(data->dev);
++ regulator_bulk_disable(data->num_vregs, data->regulators);
++
++ return 0;
++}
++
++static int qca639x_probe(struct platform_device *pdev)
++{
++ struct qca639x_data *data;
++ struct device *dev = &pdev->dev;
++ int i, ret;
++
++ if (!dev->pins || IS_ERR_OR_NULL(dev->pins->default_state))
++ return -EINVAL;
++
++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
++ data->dev = dev;
++ data->num_vregs = ARRAY_SIZE(vregs);
++
++ data->active_state = pinctrl_lookup_state(dev->pins->p, "active");
++ if (IS_ERR(data->active_state)) {
++ ret = PTR_ERR(data->active_state);
++ dev_err(dev, "Failed to get active_state: %d\n", ret);
++ return ret;
++ }
++
++ for (i = 0; i < data->num_vregs; i++)
++ data->regulators[i].supply = vregs[i].name;
++ ret = devm_regulator_bulk_get(dev, data->num_vregs, data->regulators);
++ if (ret < 0)
++ return ret;
++
++ for (i = 0; i < data->num_vregs; i++) {
++ ret = regulator_set_load(data->regulators[i].consumer, vregs[i].load_uA);
++ if (ret)
++ return ret;
++ }
++
++ data->pd.name = dev_name(dev);
++ data->pd.power_on = qca639x_power_on;
++ data->pd.power_off = qca639x_power_off;
++
++ ret = pm_genpd_init(&data->pd, NULL, true);
++ if (ret < 0)
++ return ret;
++
++ ret = of_genpd_add_provider_simple(dev->of_node, &data->pd);
++ if (ret < 0) {
++ pm_genpd_remove(&data->pd);
++ return ret;
++ }
++
++ platform_set_drvdata(pdev, data);
++
++ return 0;
++}
++
++static int qca639x_remove(struct platform_device *pdev)
++{
++ struct qca639x_data *data = platform_get_drvdata(pdev);
++
++ pm_genpd_remove(&data->pd);
++
++ return 0;
++}
++
++static const struct of_device_id qca639x_of_match[] = {
++ { .compatible = "qcom,qca639x" },
++};
++
++static struct platform_driver qca639x_driver = {
++ .probe = qca639x_probe,
++ .remove = qca639x_remove,
++ .driver = {
++ .name = "qca639x",
++ .of_match_table = qca639x_of_match,
++ },
++};
++
++module_platform_driver(qca639x_driver);
++MODULE_LICENSE("GPL v2");
+--
+2.39.2
+
diff --git a/recipes-kernel/linux/linux-yocto/qca6390-driver/0003-mfd-qcom-qca639x-switch-to-platform-config-data.patch b/recipes-kernel/linux/linux-yocto/qca6390-driver/0003-mfd-qcom-qca639x-switch-to-platform-config-data.patch
new file mode 100644
index 0000000..72458ef
--- /dev/null
+++ b/recipes-kernel/linux/linux-yocto/qca6390-driver/0003-mfd-qcom-qca639x-switch-to-platform-config-data.patch
@@ -0,0 +1,189 @@
+From bf19679f9a583a5bfd0cb711984fbe456af652fd Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Sat, 26 Feb 2022 21:13:18 +0300
+Subject: [PATCH 3/5] mfd: qcom-qca639x: switch to platform config data
+
+Change qcom-qca639x to use platform config data, in preparation to
+supporting other devices.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Upstream-Status: Inappropriate
+---
+ drivers/mfd/qcom-qca639x.c | 74 +++++++++++++++++++++++---------------
+ 1 file changed, 46 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/mfd/qcom-qca639x.c b/drivers/mfd/qcom-qca639x.c
+index b31e4b65bec5..22792561dbad 100644
+--- a/drivers/mfd/qcom-qca639x.c
++++ b/drivers/mfd/qcom-qca639x.c
+@@ -1,4 +1,5 @@
+ #include <linux/delay.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+@@ -6,15 +7,21 @@
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_domain.h>
++#include <linux/property.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+
+-#define MAX_NUM_REGULATORS 8
+-
+-static struct vreg {
++struct vreg {
+ const char *name;
+ unsigned int load_uA;
+-} vregs [MAX_NUM_REGULATORS] = {
++};
++
++struct qca_cfg_data {
++ const struct vreg *vregs;
++ size_t num_vregs;
++};
++
++static const struct vreg qca6390_vregs[] = {
+ /* 2.0 V */
+ { "vddpcie2", 15000 },
+ { "vddrfa3", 400000 },
+@@ -32,19 +39,24 @@ static struct vreg {
+ { "vddio", 20000 },
+ };
+
+-struct qca639x_data {
+- struct regulator_bulk_data regulators[MAX_NUM_REGULATORS];
++static const struct qca_cfg_data qca6390_cfg_data = {
++ .vregs = qca6390_vregs,
++ .num_vregs = ARRAY_SIZE(qca6390_vregs),
++};
++
++struct qca_data {
+ size_t num_vregs;
+ struct device *dev;
+ struct pinctrl_state *active_state;
+ struct generic_pm_domain pd;
++ struct regulator_bulk_data regulators[];
+ };
+
+-#define domain_to_data(domain) container_of(domain, struct qca639x_data, pd)
++#define domain_to_data(domain) container_of(domain, struct qca_data, pd)
+
+-static int qca639x_power_on(struct generic_pm_domain *domain)
++static int qca_power_on(struct generic_pm_domain *domain)
+ {
+- struct qca639x_data *data = domain_to_data(domain);
++ struct qca_data *data = domain_to_data(domain);
+ int ret;
+
+ dev_warn(&domain->dev, "DUMMY POWER ON\n");
+@@ -70,9 +82,9 @@ static int qca639x_power_on(struct generic_pm_domain *domain)
+ return 0;
+ }
+
+-static int qca639x_power_off(struct generic_pm_domain *domain)
++static int qca_power_off(struct generic_pm_domain *domain)
+ {
+- struct qca639x_data *data = domain_to_data(domain);
++ struct qca_data *data = domain_to_data(domain);
+
+ dev_warn(&domain->dev, "DUMMY POWER OFF\n");
+
+@@ -82,21 +94,26 @@ static int qca639x_power_off(struct generic_pm_domain *domain)
+ return 0;
+ }
+
+-static int qca639x_probe(struct platform_device *pdev)
++static int qca_probe(struct platform_device *pdev)
+ {
+- struct qca639x_data *data;
++ const struct qca_cfg_data *cfg;
++ struct qca_data *data;
+ struct device *dev = &pdev->dev;
+ int i, ret;
+
++ cfg = device_get_match_data(&pdev->dev);
++ if (!cfg)
++ return -EINVAL;
++
+ if (!dev->pins || IS_ERR_OR_NULL(dev->pins->default_state))
+ return -EINVAL;
+
+- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
++ data = devm_kzalloc(dev, struct_size(data, regulators, cfg->num_vregs), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+- data->num_vregs = ARRAY_SIZE(vregs);
++ data->num_vregs = cfg->num_vregs;
+
+ data->active_state = pinctrl_lookup_state(dev->pins->p, "active");
+ if (IS_ERR(data->active_state)) {
+@@ -106,20 +123,20 @@ static int qca639x_probe(struct platform_device *pdev)
+ }
+
+ for (i = 0; i < data->num_vregs; i++)
+- data->regulators[i].supply = vregs[i].name;
++ data->regulators[i].supply = cfg->vregs[i].name;
+ ret = devm_regulator_bulk_get(dev, data->num_vregs, data->regulators);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < data->num_vregs; i++) {
+- ret = regulator_set_load(data->regulators[i].consumer, vregs[i].load_uA);
++ ret = regulator_set_load(data->regulators[i].consumer, cfg->vregs[i].load_uA);
+ if (ret)
+ return ret;
+ }
+
+ data->pd.name = dev_name(dev);
+- data->pd.power_on = qca639x_power_on;
+- data->pd.power_off = qca639x_power_off;
++ data->pd.power_on = qca_power_on;
++ data->pd.power_off = qca_power_off;
+
+ ret = pm_genpd_init(&data->pd, NULL, true);
+ if (ret < 0)
+@@ -136,27 +153,28 @@ static int qca639x_probe(struct platform_device *pdev)
+ return 0;
+ }
+
+-static int qca639x_remove(struct platform_device *pdev)
++static int qca_remove(struct platform_device *pdev)
+ {
+- struct qca639x_data *data = platform_get_drvdata(pdev);
++ struct qca_data *data = platform_get_drvdata(pdev);
+
+ pm_genpd_remove(&data->pd);
+
+ return 0;
+ }
+
+-static const struct of_device_id qca639x_of_match[] = {
+- { .compatible = "qcom,qca639x" },
++static const struct of_device_id qca_of_match[] = {
++ { .compatible = "qcom,qca6390", .data = &qca6390_cfg_data },
++ { },
+ };
+
+-static struct platform_driver qca639x_driver = {
+- .probe = qca639x_probe,
+- .remove = qca639x_remove,
++static struct platform_driver qca_driver = {
++ .probe = qca_probe,
++ .remove = qca_remove,
+ .driver = {
+ .name = "qca639x",
+- .of_match_table = qca639x_of_match,
++ .of_match_table = qca_of_match,
+ },
+ };
+
+-module_platform_driver(qca639x_driver);
++module_platform_driver(qca_driver);
+ MODULE_LICENSE("GPL v2");
+--
+2.39.2
+
diff --git a/recipes-kernel/linux/linux-yocto/qca6390-driver/0004-mfd-qcom-qca639x-change-qca639x-to-use-gpios-rather-.patch b/recipes-kernel/linux/linux-yocto/qca6390-driver/0004-mfd-qcom-qca639x-change-qca639x-to-use-gpios-rather-.patch
new file mode 100644
index 0000000..4520e37
--- /dev/null
+++ b/recipes-kernel/linux/linux-yocto/qca6390-driver/0004-mfd-qcom-qca639x-change-qca639x-to-use-gpios-rather-.patch
@@ -0,0 +1,90 @@
+From 3f07b11f1bf49c153df0248de9128ffdad0792f8 Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Sat, 26 Feb 2022 21:17:22 +0300
+Subject: [PATCH 4/5] mfd: qcom-qca639x: change qca639x to use gpios rather
+ than pinctrl
+
+Use gpio interface instead of pinctrl interface to toggle enable pins.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Upstream-Status: Inappropriate
+---
+ drivers/mfd/qcom-qca639x.c | 33 +++++++++++++++++++--------------
+ 1 file changed, 19 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/mfd/qcom-qca639x.c b/drivers/mfd/qcom-qca639x.c
+index 22792561dbad..4de860e9bbd0 100644
+--- a/drivers/mfd/qcom-qca639x.c
++++ b/drivers/mfd/qcom-qca639x.c
+@@ -47,8 +47,9 @@ static const struct qca_cfg_data qca6390_cfg_data = {
+ struct qca_data {
+ size_t num_vregs;
+ struct device *dev;
+- struct pinctrl_state *active_state;
+ struct generic_pm_domain pd;
++ struct gpio_desc *wlan_en_gpio;
++ struct gpio_desc *bt_en_gpio;
+ struct regulator_bulk_data regulators[];
+ };
+
+@@ -70,11 +71,10 @@ static int qca_power_on(struct generic_pm_domain *domain)
+ /* Wait for 1ms before toggling enable pins. */
+ msleep(1);
+
+- ret = pinctrl_select_state(data->dev->pins->p, data->active_state);
+- if (ret) {
+- dev_err(data->dev, "Failed to select active state");
+- return ret;
+- }
++ if (data->wlan_en_gpio)
++ gpiod_set_value(data->wlan_en_gpio, 1);
++ if (data->bt_en_gpio)
++ gpiod_set_value(data->bt_en_gpio, 1);
+
+ /* Wait for all power levels to stabilize */
+ msleep(6);
+@@ -88,7 +88,11 @@ static int qca_power_off(struct generic_pm_domain *domain)
+
+ dev_warn(&domain->dev, "DUMMY POWER OFF\n");
+
+- pinctrl_select_default_state(data->dev);
++ if (data->wlan_en_gpio)
++ gpiod_set_value(data->wlan_en_gpio, 0);
++ if (data->bt_en_gpio)
++ gpiod_set_value(data->bt_en_gpio, 0);
++
+ regulator_bulk_disable(data->num_vregs, data->regulators);
+
+ return 0;
+@@ -115,13 +119,6 @@ static int qca_probe(struct platform_device *pdev)
+ data->dev = dev;
+ data->num_vregs = cfg->num_vregs;
+
+- data->active_state = pinctrl_lookup_state(dev->pins->p, "active");
+- if (IS_ERR(data->active_state)) {
+- ret = PTR_ERR(data->active_state);
+- dev_err(dev, "Failed to get active_state: %d\n", ret);
+- return ret;
+- }
+-
+ for (i = 0; i < data->num_vregs; i++)
+ data->regulators[i].supply = cfg->vregs[i].name;
+ ret = devm_regulator_bulk_get(dev, data->num_vregs, data->regulators);
+@@ -134,6 +131,14 @@ static int qca_probe(struct platform_device *pdev)
+ return ret;
+ }
+
++ data->wlan_en_gpio = devm_gpiod_get_optional(&pdev->dev, "wlan-en", GPIOD_OUT_LOW);
++ if (IS_ERR(data->wlan_en_gpio))
++ return PTR_ERR(data->wlan_en_gpio);
++
++ data->bt_en_gpio = devm_gpiod_get_optional(&pdev->dev, "bt-en", GPIOD_OUT_LOW);
++ if (IS_ERR(data->bt_en_gpio))
++ return PTR_ERR(data->bt_en_gpio);
++
+ data->pd.name = dev_name(dev);
+ data->pd.power_on = qca_power_on;
+ data->pd.power_off = qca_power_off;
+--
+2.39.2
+
diff --git a/recipes-kernel/linux/linux-yocto/qca6390-driver/0005-mfd-qcom-qca639x-Add-support-for-WCN6855.patch b/recipes-kernel/linux/linux-yocto/qca6390-driver/0005-mfd-qcom-qca639x-Add-support-for-WCN6855.patch
new file mode 100644
index 0000000..734e778
--- /dev/null
+++ b/recipes-kernel/linux/linux-yocto/qca6390-driver/0005-mfd-qcom-qca639x-Add-support-for-WCN6855.patch
@@ -0,0 +1,111 @@
+From 87c18e7aa2071dc0c95b76360671c3b3f2dcedec Mon Sep 17 00:00:00 2001
+From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Date: Sat, 26 Feb 2022 21:52:08 +0300
+Subject: [PATCH 5/5] mfd: qcom-qca639x: Add support for WCN6855
+
+Add support for powering up WCN6855 WiFi/BT chip.
+
+Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Upstream-Status: Inappropriate
+---
+ drivers/mfd/qcom-qca639x.c | 49 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+
+diff --git a/drivers/mfd/qcom-qca639x.c b/drivers/mfd/qcom-qca639x.c
+index 4de860e9bbd0..16ff767a34b0 100644
+--- a/drivers/mfd/qcom-qca639x.c
++++ b/drivers/mfd/qcom-qca639x.c
+@@ -44,10 +44,38 @@ static const struct qca_cfg_data qca6390_cfg_data = {
+ .num_vregs = ARRAY_SIZE(qca6390_vregs),
+ };
+
++static const struct vreg wcn6855_vregs[] = {
++ /* 2.8 V */
++ { "vddasd" }, /* external antenna switch */
++
++ /* 0.95 V */
++ { "vddaon" },
++ { "vddcx" },
++ { "vddmx" },
++
++ /* 1.9 V - 2.1 V */
++ { "vddrfa1" },
++
++ /* 1.35 V */
++ { "vddrfa2" },
++
++ /* 2.2 V, optional */
++ { "vddrfa3" },
++
++ /* 1.8 V */
++ { "vddio" },
++};
++
++static const struct qca_cfg_data wcn6855_cfg_data = {
++ .vregs = wcn6855_vregs,
++ .num_vregs = ARRAY_SIZE(wcn6855_vregs),
++};
++
+ struct qca_data {
+ size_t num_vregs;
+ struct device *dev;
+ struct generic_pm_domain pd;
++ struct gpio_desc *xo_clk_gpio;
+ struct gpio_desc *wlan_en_gpio;
+ struct gpio_desc *bt_en_gpio;
+ struct regulator_bulk_data regulators[];
+@@ -71,11 +99,24 @@ static int qca_power_on(struct generic_pm_domain *domain)
+ /* Wait for 1ms before toggling enable pins. */
+ msleep(1);
+
++ if (data->xo_clk_gpio) {
++ gpiod_set_value(data->xo_clk_gpio, 1);
++
++ /*XO CLK must be asserted for some time before WLAN_EN */
++ usleep_range(100, 200);
++ }
++
+ if (data->wlan_en_gpio)
+ gpiod_set_value(data->wlan_en_gpio, 1);
+ if (data->bt_en_gpio)
+ gpiod_set_value(data->bt_en_gpio, 1);
+
++ if (data->xo_clk_gpio) {
++ /* Assert XO CLK ~(2-5)ms before off for valid latch in HW */
++ usleep_range(2000, 5000);
++ gpiod_set_value(data->xo_clk_gpio, 0);
++ }
++
+ /* Wait for all power levels to stabilize */
+ msleep(6);
+
+@@ -126,11 +167,18 @@ static int qca_probe(struct platform_device *pdev)
+ return ret;
+
+ for (i = 0; i < data->num_vregs; i++) {
++ if (!cfg->vregs[i].load_uA)
++ continue;
++
+ ret = regulator_set_load(data->regulators[i].consumer, cfg->vregs[i].load_uA);
+ if (ret)
+ return ret;
+ }
+
++ data->xo_clk_gpio = devm_gpiod_get_optional(&pdev->dev, "xo-clk", GPIOD_OUT_LOW);
++ if (IS_ERR(data->xo_clk_gpio))
++ return PTR_ERR(data->xo_clk_gpio);
++
+ data->wlan_en_gpio = devm_gpiod_get_optional(&pdev->dev, "wlan-en", GPIOD_OUT_LOW);
+ if (IS_ERR(data->wlan_en_gpio))
+ return PTR_ERR(data->wlan_en_gpio);
+@@ -169,6 +217,7 @@ static int qca_remove(struct platform_device *pdev)
+
+ static const struct of_device_id qca_of_match[] = {
+ { .compatible = "qcom,qca6390", .data = &qca6390_cfg_data },
++ { .compatible = "qcom,wcn6855", .data = &wcn6855_cfg_data },
+ { },
+ };
+
+--
+2.39.2
+