diff options
author | Linaro CI <ci_notify@linaro.org> | 2023-03-19 20:47:06 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2023-03-19 20:47:06 +0000 |
commit | 363099e73a08a2f7cfb685b5e05151709f2866c1 (patch) | |
tree | 76778db0d9e77cbc7b9452e7319c5297eb0b3218 /drivers | |
parent | daeec3d57ecad8ac356d8ea634f165aeb08ba1f0 (diff) | |
parent | 4bbec77592e56a423bcf3a00583f79b22cdb6a00 (diff) |
Merge remote-tracking branch 'qca6390/tracking-qcomlt-qca6390' into integration-linux-qcomlt
Diffstat (limited to 'drivers')
-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 |
6 files changed, 266 insertions, 2 deletions
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"); |