aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch')
-rw-r--r--recipes-kernel/linux/linux-yocto/qca6390-driver/0002-mfd-qca639x-add-support-for-QCA639x-powerup-sequence.patch225
1 files changed, 225 insertions, 0 deletions
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
+