diff options
author | Linaro CI <ci_notify@linaro.org> | 2022-06-02 16:09:16 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2022-06-02 16:09:16 +0000 |
commit | db9bf5739522360951fea5485d05364265c5e0aa (patch) | |
tree | 10ab37fe5e802392d264e82c7da37260920ea73d | |
parent | 5df4e9ac27ab3fceba6be4e06fe4d393963b555a (diff) | |
parent | 609262bec9ed01640258d5ccfa1853dfd7234e56 (diff) |
Merge remote-tracking branch 'qca6390/tracking-qcomlt-qca6390' into integration-linux-qcomlt
-rw-r--r-- | Documentation/devicetree/bindings/mfd/qcom,qca639x.yaml | 84 | ||||
-rw-r--r-- | arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 58 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.c | 8 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.h | 1 | ||||
-rw-r--r-- | drivers/bluetooth/hci_qca.c | 12 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/qcom-qca639x.c | 234 |
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 b72909c24fd2..b3a237e41e36 100644 --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -21,6 +21,7 @@ aliases { serial0 = &uart12; + serial1 = &uart6; sdhc2 = &sdhc_2; }; @@ -224,6 +225,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 { @@ -677,6 +698,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 { @@ -1195,6 +1219,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 { pins = "gpio63"; function = "gpio"; @@ -1226,6 +1261,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 f6e91fb432a3..b32e358fcf96 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1683,6 +1683,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); } } @@ -1858,6 +1860,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 []) { @@ -2160,7 +2168,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) { serdev_device_write_flush(serdev); ret = serdev_device_write_buf(serdev, ibs_wake_cmd, sizeof(ibs_wake_cmd)); @@ -2320,6 +2329,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 3b59456f5545..31b4168d4c5f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1066,6 +1066,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 858cacf659d6..7752dcbf3104 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -198,6 +198,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"); |