aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2023-03-09 18:31:24 +0000
committerLinaro CI <ci_notify@linaro.org>2023-03-09 18:31:24 +0000
commit66d8ac6c92de403599f639b014e60e550e707cd1 (patch)
tree3185f0b708a43bfe2bb45e60b28bef821680709d
parent258bfce184273afc64795dcf861035d199038529 (diff)
parent4bbec77592e56a423bcf3a00583f79b22cdb6a00 (diff)
Merge remote-tracking branch 'qca6390/tracking-qcomlt-qca6390' into integration-linux-qcomlt
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml84
-rw-r--r--arch/arm64/boot/dts/qcom/qrb5165-rb5.dts58
-rw-r--r--drivers/bluetooth/btqca.c8
-rw-r--r--drivers/bluetooth/btqca.h1
-rw-r--r--drivers/bluetooth/hci_qca.c12
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/qcom-qca639x.c234
8 files changed, 408 insertions, 2 deletions
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>;
+ };
+...
diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index aa0a7bd7307c..1bcb1ddcb86b 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -22,6 +22,7 @@
aliases {
serial0 = &uart12;
+ serial1 = &uart6;
sdhc2 = &sdhc_2;
};
@@ -230,6 +231,26 @@
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
+
+ qca639x: qca639x {
+ compatible = "qcom,qca6390";
+ #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";
+ pinctrl-0 = <&wlan_en_state>;
+
+ wlan-en-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>;
+ };
+
};
&adsp {
@@ -683,6 +704,9 @@
status = "okay";
vdda-phy-supply = <&vreg_l5a_0p88>;
vdda-pll-supply = <&vreg_l9a_1p2>;
+
+ /* Power on QCA639x chip, otherwise PCIe bus timeouts */
+ power-domains = <&qca639x>;
};
&pcie1 {
@@ -1212,6 +1236,17 @@
"HST_WLAN_UART_TX",
"HST_WLAN_UART_RX";
+ bt_en_state: bt-default-state {
+ bt-en {
+ pins = "gpio21";
+ function = "gpio";
+
+ drive-strength = <16>;
+ output-low;
+ bias-pull-up;
+ };
+ };
+
lt9611_irq_pin: lt9611-irq-state {
pins = "gpio63";
function = "gpio";
@@ -1243,6 +1278,29 @@
function = "gpio";
bias-pull-up;
};
+
+ wlan_en_state: wlan-default-state {
+ wlan-en {
+ pins = "gpio20";
+ function = "gpio";
+
+ drive-strength = <16>;
+ output-low;
+ bias-pull-up;
+ };
+ };
+};
+
+&uart6 {
+ status = "okay";
+ bluetooth {
+ compatible = "qcom,qca6390-bt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bt_en_state>;
+
+ power-domains = <&qca639x>;
+ enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>;
+ };
};
&uart12 {
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index c9064d34d830..0150fe169cad 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -614,6 +614,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
config.type = ELF_TYPE_PATCH;
snprintf(config.fwname, sizeof(config.fwname),
"qca/msbtfw%02x.mbn", rom_ver);
+ } else if (soc_type == QCA_WCN6855) {
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/hpbtfw%02x.tlv", rom_ver);
} else {
snprintf(config.fwname, sizeof(config.fwname),
"qca/rampatch_%08x.bin", soc_ver);
@@ -648,6 +651,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
else if (soc_type == QCA_WCN6750)
snprintf(config.fwname, sizeof(config.fwname),
"qca/msnv%02x.bin", rom_ver);
+ else if (soc_type == QCA_WCN6855)
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/hpnv%02x.bin", rom_ver);
else
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
@@ -685,7 +691,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
- if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750) {
+ if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750 || soc_type == QCA_WCN6855) {
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 61e9a50e66ae..928fd4d6b10c 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -147,6 +147,7 @@ enum qca_btsoc_type {
QCA_WCN3991,
QCA_QCA6390,
QCA_WCN6750,
+ QCA_WCN6855,
};
#if IS_ENABLED(CONFIG_BT_QCA)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 3df8c3606e93..e71c373e7e5c 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1690,6 +1690,8 @@ static int qca_power_on(struct hci_dev *hdev)
gpiod_set_value_cansleep(qcadev->bt_en, 1);
/* Controller needs time to bootup. */
msleep(150);
+ serdev_device_close(hu->serdev);
+ ret = serdev_device_open(hu->serdev);
}
}
@@ -1866,6 +1868,12 @@ static const struct qca_device_data qca_soc_data_qca6390 = {
.num_vregs = 0,
};
+static const struct qca_device_data qca_soc_data_wcn6855 = {
+ .soc_type = QCA_WCN6855,
+ .num_vregs = 0,
+ .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
+};
+
static const struct qca_device_data qca_soc_data_wcn6750 = {
.soc_type = QCA_WCN6750,
.vregs = (struct qca_vreg []) {
@@ -2171,7 +2179,8 @@ static void qca_serdev_shutdown(struct device *dev)
const u8 ibs_wake_cmd[] = { 0xFD };
const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 };
- if (qcadev->btsoc_type == QCA_QCA6390) {
+ if (qcadev->btsoc_type == QCA_QCA6390 ||
+ qcadev->btsoc_type == QCA_WCN6855) {
if (test_bit(QCA_BT_OFF, &qca->flags) ||
!test_bit(HCI_RUNNING, &hdev->flags))
return;
@@ -2335,6 +2344,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
{ .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
+ { .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fcc141e067b9..f61a0dc9defe 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1079,6 +1079,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 2f6c89d1e277..fdac433ecfb7 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -196,6 +196,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..16ff767a34b0
--- /dev/null
+++ b/drivers/mfd/qcom-qca639x.c
@@ -0,0 +1,234 @@
+#include <linux/delay.h>
+#include <linux/gpio/consumer.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/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+struct vreg {
+ const char *name;
+ unsigned int load_uA;
+};
+
+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 },
+
+ /* 0.95 V */
+ { "vddaon", 100000 },
+ { "vddpmu", 1250000 },
+ { "vddrfa1", 200000 },
+
+ /* 1.35 V */
+ { "vddrfa2", 400000 },
+ { "vddpcie1", 35000 },
+
+ /* 1.8 V */
+ { "vddio", 20000 },
+};
+
+static const struct qca_cfg_data qca6390_cfg_data = {
+ .vregs = qca6390_vregs,
+ .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[];
+};
+
+#define domain_to_data(domain) container_of(domain, struct qca_data, pd)
+
+static int qca_power_on(struct generic_pm_domain *domain)
+{
+ struct qca_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);
+
+ 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);
+
+ return 0;
+}
+
+static int qca_power_off(struct generic_pm_domain *domain)
+{
+ struct qca_data *data = domain_to_data(domain);
+
+ dev_warn(&domain->dev, "DUMMY POWER OFF\n");
+
+ 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;
+}
+
+static int qca_probe(struct platform_device *pdev)
+{
+ 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, struct_size(data, regulators, cfg->num_vregs), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+ data->num_vregs = cfg->num_vregs;
+
+ 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);
+ if (ret < 0)
+ 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);
+
+ 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;
+
+ 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 qca_remove(struct platform_device *pdev)
+{
+ struct qca_data *data = platform_get_drvdata(pdev);
+
+ pm_genpd_remove(&data->pd);
+
+ return 0;
+}
+
+static const struct of_device_id qca_of_match[] = {
+ { .compatible = "qcom,qca6390", .data = &qca6390_cfg_data },
+ { .compatible = "qcom,wcn6855", .data = &wcn6855_cfg_data },
+ { },
+};
+
+static struct platform_driver qca_driver = {
+ .probe = qca_probe,
+ .remove = qca_remove,
+ .driver = {
+ .name = "qca639x",
+ .of_match_table = qca_of_match,
+ },
+};
+
+module_platform_driver(qca_driver);
+MODULE_LICENSE("GPL v2");