mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
224 lines
6.0 KiB
Diff
224 lines
6.0 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
|
|
Date: Fri, 18 Dec 2020 16:24:56 +0300
|
|
Subject: 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>
|
|
---
|
|
drivers/mfd/Kconfig | 12 +
|
|
drivers/mfd/Makefile | 1 +
|
|
drivers/mfd/qcom-qca639x.c | 162 ++++++++++
|
|
3 files changed, 175 insertions(+)
|
|
|
|
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
|
|
index 90ce58fd6..2dd393ff2 100644
|
|
--- a/drivers/mfd/Kconfig
|
|
+++ b/drivers/mfd/Kconfig
|
|
@@ -1123,6 +1123,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 c66f07edc..3e1b32dd0 100644
|
|
--- a/drivers/mfd/Makefile
|
|
+++ b/drivers/mfd/Makefile
|
|
@@ -204,6 +204,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 000000000..b31e4b65b
|
|
--- /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");
|
|
--
|
|
Armbian
|
|
|