Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions Documentation/devicetree/bindings/regulator/qcom,wcn3990-pmu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/qcom,wcn3990-pmu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Qualcomm Technologies, Inc. WCN3990 PMU Regulators

maintainers:
- Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

description:
The WCN3990 package contains discrete modules for WLAN and Bluetooth. They
are powered by the Power Management Unit (PMU) that takes inputs from the
host and provides LDO outputs. This document describes this module.

properties:
compatible:
enum:
- qcom,wcn3950-pmu
- qcom,wcn3988-pmu
- qcom,wcn3990-pmu
- qcom,wcn3991-pmu
- qcom,wcn3998-pmu

vddio-supply:
description: VDD_IO supply regulator handle

vddxo-supply:
description: VDD_XTAL supply regulator handle

vddrf-supply:
description: VDD_RF supply regulator handle

vddch0-supply:
description: chain 0 supply regulator handle

vddch1-supply:
description: chain 1 supply regulator handle

swctrl-gpios:
maxItems: 1
description: GPIO line indicating the state of the clock supply to the BT module

clocks:
maxItems: 1
description: Reference clock handle

regulators:
type: object
description:
LDO outputs of the PMU

patternProperties:
"^ldo[0-9]$":
$ref: regulator.yaml#
type: object
unevaluatedProperties: false

additionalProperties: false

required:
- compatible
- regulators
- vddio-supply
- vddxo-supply
- vddrf-supply
- vddch0-supply

additionalProperties: false

examples:
- |
#include <dt-bindings/gpio/gpio.h>
pmu {
compatible = "qcom,wcn3990-pmu";

vddio-supply = <&vreg_io>;
vddxo-supply = <&vreg_xo>;
vddrf-supply = <&vreg_rf>;
vddch0-supply = <&vreg_ch0>;

regulators {
vreg_pmu_io: ldo0 {
regulator-name = "vreg_pmu_io";
};

vreg_pmu_xo: ldo1 {
regulator-name = "vreg_pmu_xo";
};

vreg_pmu_rf: ldo2 {
regulator-name = "vreg_pmu_rf";
};

vreg_pmu_ch0: ldo3 {
regulator-name = "vreg_pmu_ch0";
};
};
};
26 changes: 18 additions & 8 deletions drivers/bluetooth/hci_qca.c
Original file line number Diff line number Diff line change
Expand Up @@ -2254,6 +2254,18 @@ static void qca_power_shutdown(struct hci_uart *hu)
qcadev = serdev_device_get_drvdata(hu->serdev);
power = qcadev->bt_power;

switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
break;
default:
break;
}

if (power && power->pwrseq) {
pwrseq_power_off(power->pwrseq);
set_bit(QCA_BT_OFF, &qca->flags);
Expand All @@ -2265,8 +2277,6 @@ static void qca_power_shutdown(struct hci_uart *hu)
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
qca_regulator_disable(qcadev);
break;

Expand Down Expand Up @@ -2441,6 +2451,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)

switch (qcadev->btsoc_type) {
case QCA_QCC2072:
case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6855:
case QCA_WCN7850:
case QCA_WCN6750:
Expand All @@ -2465,12 +2480,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
else
break;
}
fallthrough;
case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:

qcadev->bt_power->dev = &serdev->dev;
err = qca_init_regulators(qcadev->bt_power, data->vregs,
data->num_vregs);
Expand Down
130 changes: 125 additions & 5 deletions drivers/power/sequencing/pwrseq-qcom-wcn.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ struct pwrseq_qcom_wcn_pdata {
unsigned int pwup_delay_ms;
unsigned int gpio_enable_delay_ms;
const struct pwrseq_target_data **targets;
bool has_vddio; /* separate VDD IO regulator */
int (*match)(struct pwrseq_device *pwrseq, struct device *dev);
};

struct pwrseq_qcom_wcn_ctx {
struct pwrseq_device *pwrseq;
struct device_node *of_node;
const struct pwrseq_qcom_wcn_pdata *pdata;
struct regulator_bulk_data *regs;
struct regulator *vddio;
struct gpio_desc *bt_gpio;
struct gpio_desc *wlan_gpio;
struct gpio_desc *xo_clk_gpio;
Expand All @@ -52,6 +55,26 @@ static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx)
msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs);
}

static int pwrseq_qcom_wcn_vddio_enable(struct pwrseq_device *pwrseq)
{
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);

return regulator_enable(ctx->vddio);
}

static int pwrseq_qcom_wcn_vddio_disable(struct pwrseq_device *pwrseq)
{
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);

return regulator_disable(ctx->vddio);
}

static const struct pwrseq_unit_data pwrseq_qcom_wcn_vddio_unit_data = {
.name = "vddio-enable",
.enable = pwrseq_qcom_wcn_vddio_enable,
.disable = pwrseq_qcom_wcn_vddio_disable,
};

static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq)
{
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
Expand Down Expand Up @@ -94,6 +117,19 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = {
.disable = pwrseq_qcom_wcn_clk_disable,
};

static const struct pwrseq_unit_data *pwrseq_qcom_wcn3990_unit_deps[] = {
&pwrseq_qcom_wcn_vddio_unit_data,
&pwrseq_qcom_wcn_vregs_unit_data,
NULL,
};

static const struct pwrseq_unit_data pwrseq_qcom_wcn3990_unit_data = {
.name = "clock-enable",
.deps = pwrseq_qcom_wcn3990_unit_deps,
.enable = pwrseq_qcom_wcn_clk_enable,
.disable = pwrseq_qcom_wcn_clk_disable,
};

static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = {
&pwrseq_qcom_wcn_vregs_unit_data,
&pwrseq_qcom_wcn_clk_unit_data,
Expand Down Expand Up @@ -229,6 +265,17 @@ static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = {
.post_enable = pwrseq_qcom_wcn_pwup_delay,
};

/* There are no separate BT and WLAN enablement pins */
static const struct pwrseq_target_data pwrseq_qcom_wcn3990_bt_target_data = {
.name = "bluetooth",
.unit = &pwrseq_qcom_wcn3990_unit_data,
};

static const struct pwrseq_target_data pwrseq_qcom_wcn3990_wlan_target_data = {
.name = "wlan",
.unit = &pwrseq_qcom_wcn3990_unit_data,
};

static const struct pwrseq_target_data pwrseq_qcom_wcn6855_bt_target_data = {
.name = "bluetooth",
.unit = &pwrseq_qcom_wcn6855_bt_unit_data,
Expand All @@ -247,6 +294,12 @@ static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = {
NULL
};

static const struct pwrseq_target_data *pwrseq_qcom_wcn3990_targets[] = {
&pwrseq_qcom_wcn3990_bt_target_data,
&pwrseq_qcom_wcn3990_wlan_target_data,
NULL
};

static const struct pwrseq_target_data *pwrseq_qcom_wcn6855_targets[] = {
&pwrseq_qcom_wcn6855_bt_target_data,
&pwrseq_qcom_wcn6855_wlan_target_data,
Expand All @@ -272,6 +325,26 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = {
.targets = pwrseq_qcom_wcn_targets,
};

static const char *const pwrseq_wcn3990_vregs[] = {
/* vddio is handled separately */
"vddxo",
"vddrf",
"vddch0",
"vddch1",
};

static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
struct device *dev);

static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn3990_of_data = {
.vregs = pwrseq_wcn3990_vregs,
.num_vregs = ARRAY_SIZE(pwrseq_wcn3990_vregs),
.pwup_delay_ms = 50,
.targets = pwrseq_qcom_wcn3990_targets,
.has_vddio = true,
.match = pwrseq_qcom_wcn3990_match,
};

static const char *const pwrseq_wcn6750_vregs[] = {
"vddaon",
"vddasd",
Expand Down Expand Up @@ -328,8 +401,9 @@ static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = {
.targets = pwrseq_qcom_wcn_targets,
};

static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
struct device *dev)
static int pwrseq_qcom_wcn_match_regulator(struct pwrseq_device *pwrseq,
struct device *dev,
const char *name)
{
struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
struct device_node *dev_node = dev->of_node;
Expand All @@ -340,11 +414,11 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
* 'vddaon-supply' property and whether it leads us to the right
* device.
*/
if (!of_property_present(dev_node, "vddaon-supply"))
if (!of_property_present(dev_node, name))
return PWRSEQ_NO_MATCH;

struct device_node *reg_node __free(device_node) =
of_parse_phandle(dev_node, "vddaon-supply", 0);
of_parse_phandle(dev_node, name, 0);
if (!reg_node)
return PWRSEQ_NO_MATCH;

Expand Down Expand Up @@ -374,6 +448,26 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
return PWRSEQ_MATCH_OK;
}

static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
struct device *dev)
{
return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddaon-supply");
}

static int pwrseq_qcom_wcn3990_match(struct pwrseq_device *pwrseq,
struct device *dev)
{
int ret;

/* BT device */
ret = pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vddio-supply");
if (ret == PWRSEQ_MATCH_OK)
return ret;

/* WiFi device match */
return pwrseq_qcom_wcn_match_regulator(pwrseq, dev, "vdd-1.8-xo-supply");
}

static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
Expand Down Expand Up @@ -405,6 +499,12 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret,
"Failed to get all regulators\n");

if (ctx->pdata->has_vddio) {
ctx->vddio = devm_regulator_get(dev, "vddio");
if (IS_ERR(ctx->vddio))
return dev_err_probe(dev, ret, "Failed to get VDDIO\n");
}

ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW);
if (IS_ERR(ctx->bt_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio),
Expand Down Expand Up @@ -446,7 +546,7 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
config.parent = dev;
config.owner = THIS_MODULE;
config.drvdata = ctx;
config.match = pwrseq_qcom_wcn_match;
config.match = ctx->pdata->match ? : pwrseq_qcom_wcn_match;
config.targets = ctx->pdata->targets;

ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
Expand All @@ -458,6 +558,26 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
}

static const struct of_device_id pwrseq_qcom_wcn_of_match[] = {
{
.compatible = "qcom,wcn3950-pmu",
.data = &pwrseq_wcn3990_of_data,
},
{
.compatible = "qcom,wcn3988-pmu",
.data = &pwrseq_wcn3990_of_data,
},
{
.compatible = "qcom,wcn3990-pmu",
.data = &pwrseq_wcn3990_of_data,
},
{
.compatible = "qcom,wcn3991-pmu",
.data = &pwrseq_wcn3990_of_data,
},
{
.compatible = "qcom,wcn3998-pmu",
.data = &pwrseq_wcn3990_of_data,
},
{
.compatible = "qcom,qca6390-pmu",
.data = &pwrseq_qca6390_of_data,
Expand Down