mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:24:45 +01:00
power sequencing updates for v7.0-rc1
New drivers: - add the power sequencing driver for PCIe M.2 connectors Driver improvements: - use device_get_match_data() where applicable - add support for the WCN39xx family of models to pwrseq-qcom-wcn Fixes: - fix a locking issue in pwrseq core - fix retval check in pwrseq-qcom-wcn -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEkeUTLeW1Rh17omX8BZ0uy/82hMMFAmmK8IkACgkQBZ0uy/82 hMMxMxAAqDCSDB+faKHMKDoHN6Jwpn57y2ODAP7BGwoDdET/EVU6qrij2+gmHpNF Vgzk5hvSxv8cCzncPDPvtDoBWCNBN+6YKHW1M4aweo72wwLnhmBXYGkGV4DVqY2k u2afURTuSR6DsC/J2wtOQmz+TSbgwE7QbevPkJOaMR1FrahUp8KT3zgHYqRcCWcJ xm4Hx1w2xHMQ87+d5IK03gFfyHE/gXBKmAFH5xno/7Gwev6oCS3DulFI97tlq4lO YGAiVkxkYrOjHNDxw1+ePIRuVl992nNQmuahyMpujtUXfb6UxQ0ZpqxTk5wbaERX sZEp8AW1kpgwd2CzJqXrG/gTIBU+1dB5iDZDpviR+QeZzDFafsBiEUwS1sTfZsaP k22vMmhrqjxqFB1FwksyBLfbvE5+0uknzEjXkC1gwMea/0q2L8S8av1aMdJMTl9k iphsCKaRscfPPLNU7nHczrtj9KgSh7tSa6iAe29cyz+RPMVgXgJ0shaOWH/vEmKe sRsGfCPESBa5yEw8/gZqNs1WLd6jM3Xxuzh/KJnz9Z6zB5o1AiMyfC4oCV7BK+WP OdtLyya+2R07ZwuDPW4M2yY9pGgIrULbJJohlxb8mxL+dwZZk2pHWBvxqjlKB2B8 NhpfW53w00ZV911KPAreGTLNHbDGJOHOaUkSD5eyAL9Lroq8frA= =1SR6 -----END PGP SIGNATURE----- Merge tag 'pwrseq-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux Pull power sequencing updates from Bartosz Golaszewski: "One new driver and support for more models added to the existing qcom-wcn driver as well as some minor tweaks and fixes. New drivers: - add the power sequencing driver for PCIe M.2 connectors Driver improvements: - use device_get_match_data() where applicable - add support for the WCN39xx family of models to pwrseq-qcom-wcn Fixes: - fix a locking issue in pwrseq core - fix retval check in pwrseq-qcom-wcn" * tag 'pwrseq-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: power: sequencing: qcom-wcn: fix error path for VDDIO handling power: sequencing: fix missing state_lock in pwrseq_power_on() error path power: sequencing: Add the Power Sequencing driver for the PCIe M.2 connectors dt-bindings: connector: Add PCIe M.2 Mechanical Key M connector power: sequencing: qcom-wcn: add support for WCN39xx regulator: dt-bindings: qcom,wcn3990-pmu: describe PMUs on WCN39xx power: sequencing: qcom-wcn: use device_get_match_data()
This commit is contained in:
commit
893ace4df0
8 changed files with 560 additions and 8 deletions
|
|
@ -0,0 +1,145 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/connector/pcie-m2-m-connector.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: PCIe M.2 Mechanical Key M Connector
|
||||
|
||||
maintainers:
|
||||
- Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
|
||||
|
||||
description:
|
||||
A PCIe M.2 M connector node represents a physical PCIe M.2 Mechanical Key M
|
||||
connector. The Mechanical Key M connectors are used to connect SSDs to the
|
||||
host system over PCIe/SATA interfaces. These connectors also offer optional
|
||||
interfaces like USB, SMBus.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: pcie-m2-m-connector
|
||||
|
||||
vpcie3v3-supply:
|
||||
description: A phandle to the regulator for 3.3v supply.
|
||||
|
||||
vpcie1v8-supply:
|
||||
description: A phandle to the regulator for VIO 1.8v supply.
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
description: OF graph bindings modeling the interfaces exposed on the
|
||||
connector. Since a single connector can have multiple interfaces, every
|
||||
interface has an assigned OF graph port number as described below.
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: PCIe interface
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: SATA interface
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: USB 2.0 interface
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- port@0
|
||||
- required:
|
||||
- port@1
|
||||
|
||||
i2c-parent:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: I2C interface
|
||||
|
||||
clocks:
|
||||
description: 32.768 KHz Suspend Clock (SUSCLK) input from the host system to
|
||||
the M.2 card. Refer, PCI Express M.2 Specification r4.0, sec 3.1.12.1 for
|
||||
more details.
|
||||
maxItems: 1
|
||||
|
||||
pedet-gpios:
|
||||
description: GPIO input to PEDET signal. This signal is used by the host
|
||||
systems to determine the communication protocol that the M.2 card uses;
|
||||
SATA signaling (low) or PCIe signaling (high). Refer, PCI Express M.2
|
||||
Specification r4.0, sec 3.3.4.2 for more details.
|
||||
maxItems: 1
|
||||
|
||||
viocfg-gpios:
|
||||
description: GPIO input to IO voltage configuration (VIO_CFG) signal. This
|
||||
signal is used by the host systems to determine whether the card supports
|
||||
an independent IO voltage domain for the sideband signals or not. Refer,
|
||||
PCI Express M.2 Specification r4.0, sec 3.1.15.1 for more details.
|
||||
maxItems: 1
|
||||
|
||||
pwrdis-gpios:
|
||||
description: GPIO output to Power Disable (PWRDIS) signal. This signal is
|
||||
used by the host system to disable power on the M.2 card. Refer, PCI
|
||||
Express M.2 Specification r4.0, sec 3.3.5.2 for more details.
|
||||
maxItems: 1
|
||||
|
||||
pln-gpios:
|
||||
description: GPIO output to Power Loss Notification (PLN#) signal. This
|
||||
signal is used by the host system to notify the M.2 card that the power
|
||||
loss event is about to occur. Refer, PCI Express M.2 Specification r4.0,
|
||||
sec 3.2.17.1 for more details.
|
||||
maxItems: 1
|
||||
|
||||
plas3-gpios:
|
||||
description: GPIO input to Power Loss Acknowledge (PLA_S3#) signal. This
|
||||
signal is used by the host system to receive the acknowledgment of the M.2
|
||||
card's preparation for power loss.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vpcie3v3-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# PCI M.2 Key M connector for SSDs with PCIe interface
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
connector {
|
||||
compatible = "pcie-m2-m-connector";
|
||||
vpcie3v3-supply = <&vreg_nvme>;
|
||||
i2c-parent = <&i2c0>;
|
||||
pedet-gpios = <&tlmm 95 GPIO_ACTIVE_HIGH>;
|
||||
viocfg-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>;
|
||||
pwrdis-gpios = <&tlmm 97 GPIO_ACTIVE_HIGH>;
|
||||
pln-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>;
|
||||
plas3-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reg = <0>;
|
||||
|
||||
endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&pcie6_port0_ep>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
reg = <2>;
|
||||
|
||||
endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&usb_hs_ep>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -20861,6 +20861,13 @@ F: Documentation/driver-api/pwrseq.rst
|
|||
F: drivers/power/sequencing/
|
||||
F: include/linux/pwrseq/
|
||||
|
||||
PCIE M.2 POWER SEQUENCING
|
||||
M: Manivannan Sadhasivam <mani@kernel.org>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/connector/pcie-m2-m-connector.yaml
|
||||
F: drivers/power/sequencing/pwrseq-pcie-m2.c
|
||||
|
||||
POWER STATE COORDINATION INTERFACE (PSCI)
|
||||
M: Mark Rutland <mark.rutland@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
|
|
|
|||
|
|
@ -35,4 +35,12 @@ config POWER_SEQUENCING_TH1520_GPU
|
|||
GPU. This driver handles the complex clock and reset sequence
|
||||
required to power on the Imagination BXM GPU on this platform.
|
||||
|
||||
config POWER_SEQUENCING_PCIE_M2
|
||||
tristate "PCIe M.2 connector power sequencing driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable the power sequencing driver for PCIe M.2
|
||||
connectors. This driver handles the power sequencing for the M.2
|
||||
connectors exposing multiple interfaces like PCIe, SATA, UART, etc...
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ pwrseq-core-y := core.o
|
|||
|
||||
obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o
|
||||
obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o
|
||||
obj-$(CONFIG_POWER_SEQUENCING_PCIE_M2) += pwrseq-pcie-m2.o
|
||||
|
|
|
|||
|
|
@ -914,8 +914,10 @@ int pwrseq_power_on(struct pwrseq_desc *desc)
|
|||
if (target->post_enable) {
|
||||
ret = target->post_enable(pwrseq);
|
||||
if (ret) {
|
||||
pwrseq_unit_disable(pwrseq, unit);
|
||||
desc->powered_on = false;
|
||||
scoped_guard(mutex, &pwrseq->state_lock) {
|
||||
pwrseq_unit_disable(pwrseq, unit);
|
||||
desc->powered_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
168
drivers/power/sequencing/pwrseq-pcie-m2.c
Normal file
168
drivers/power/sequencing/pwrseq-pcie-m2.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwrseq/provider.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct pwrseq_pcie_m2_pdata {
|
||||
const struct pwrseq_target_data **targets;
|
||||
};
|
||||
|
||||
struct pwrseq_pcie_m2_ctx {
|
||||
struct pwrseq_device *pwrseq;
|
||||
struct device_node *of_node;
|
||||
const struct pwrseq_pcie_m2_pdata *pdata;
|
||||
struct regulator_bulk_data *regs;
|
||||
size_t num_vregs;
|
||||
struct notifier_block nb;
|
||||
};
|
||||
|
||||
static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq)
|
||||
{
|
||||
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
|
||||
return regulator_bulk_enable(ctx->num_vregs, ctx->regs);
|
||||
}
|
||||
|
||||
static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq)
|
||||
{
|
||||
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
|
||||
return regulator_bulk_disable(ctx->num_vregs, ctx->regs);
|
||||
}
|
||||
|
||||
static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = {
|
||||
.name = "regulators-enable",
|
||||
.enable = pwrseq_pcie_m2_m_vregs_enable,
|
||||
.disable = pwrseq_pcie_m2_m_vregs_disable,
|
||||
};
|
||||
|
||||
static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = {
|
||||
&pwrseq_pcie_m2_vregs_unit_data,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = {
|
||||
.name = "pcie-enable",
|
||||
.deps = pwrseq_pcie_m2_m_unit_deps,
|
||||
};
|
||||
|
||||
static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = {
|
||||
.name = "pcie",
|
||||
.unit = &pwrseq_pcie_m2_m_pcie_unit_data,
|
||||
};
|
||||
|
||||
static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = {
|
||||
&pwrseq_pcie_m2_m_pcie_target_data,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = {
|
||||
.targets = pwrseq_pcie_m2_m_targets,
|
||||
};
|
||||
|
||||
static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
|
||||
struct device *dev)
|
||||
{
|
||||
struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
|
||||
struct device_node *endpoint __free(device_node) = NULL;
|
||||
|
||||
/*
|
||||
* Traverse the 'remote-endpoint' nodes and check if the remote node's
|
||||
* parent matches the OF node of 'dev'.
|
||||
*/
|
||||
for_each_endpoint_of_node(ctx->of_node, endpoint) {
|
||||
struct device_node *remote __free(device_node) =
|
||||
of_graph_get_remote_port_parent(endpoint);
|
||||
if (remote && (remote == dev_of_node(dev)))
|
||||
return PWRSEQ_MATCH_OK;
|
||||
}
|
||||
|
||||
return PWRSEQ_NO_MATCH;
|
||||
}
|
||||
|
||||
static void pwrseq_pcie_m2_free_regulators(void *data)
|
||||
{
|
||||
struct pwrseq_pcie_m2_ctx *ctx = data;
|
||||
|
||||
regulator_bulk_free(ctx->num_vregs, ctx->regs);
|
||||
}
|
||||
|
||||
static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pwrseq_pcie_m2_ctx *ctx;
|
||||
struct pwrseq_config config = {};
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->of_node = of_node_get(dev->of_node);
|
||||
ctx->pdata = device_get_match_data(dev);
|
||||
if (!ctx->pdata)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"Failed to obtain platform data\n");
|
||||
|
||||
/*
|
||||
* Currently, of_regulator_bulk_get_all() is the only regulator API that
|
||||
* allows to get all supplies in the devicetree node without manually
|
||||
* specifying them.
|
||||
*/
|
||||
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &ctx->regs);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to get all regulators\n");
|
||||
|
||||
ctx->num_vregs = ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pwrseq_pcie_m2_free_regulators, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
config.parent = dev;
|
||||
config.owner = THIS_MODULE;
|
||||
config.drvdata = ctx;
|
||||
config.match = pwrseq_pcie_m2_match;
|
||||
config.targets = ctx->pdata->targets;
|
||||
|
||||
ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
|
||||
if (IS_ERR(ctx->pwrseq))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
|
||||
"Failed to register the power sequencer\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pwrseq_pcie_m2_of_match[] = {
|
||||
{
|
||||
.compatible = "pcie-m2-m-connector",
|
||||
.data = &pwrseq_pcie_m2_m_of_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match);
|
||||
|
||||
static struct platform_driver pwrseq_pcie_m2_driver = {
|
||||
.driver = {
|
||||
.name = "pwrseq-pcie-m2",
|
||||
.of_match_table = pwrseq_pcie_m2_of_match,
|
||||
},
|
||||
.probe = pwrseq_pcie_m2_probe,
|
||||
};
|
||||
module_platform_driver(pwrseq_pcie_m2_driver);
|
||||
|
||||
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>");
|
||||
MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pwrseq/provider.h>
|
||||
#include <linux/string.h>
|
||||
|
|
@ -23,6 +24,8 @@ 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 {
|
||||
|
|
@ -30,6 +33,7 @@ struct pwrseq_qcom_wcn_ctx {
|
|||
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;
|
||||
|
|
@ -52,6 +56,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);
|
||||
|
|
@ -94,6 +118,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,
|
||||
|
|
@ -229,6 +266,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,
|
||||
|
|
@ -247,6 +295,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,
|
||||
|
|
@ -272,6 +326,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",
|
||||
|
|
@ -328,8 +402,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;
|
||||
|
|
@ -340,11 +415,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;
|
||||
|
||||
|
|
@ -360,6 +435,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;
|
||||
|
|
@ -373,7 +468,7 @@ static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
|
|||
|
||||
ctx->of_node = dev->of_node;
|
||||
|
||||
ctx->pdata = of_device_get_match_data(dev);
|
||||
ctx->pdata = device_get_match_data(dev);
|
||||
if (!ctx->pdata)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"Failed to obtain platform data\n");
|
||||
|
|
@ -391,6 +486,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, PTR_ERR(ctx->vddio), "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),
|
||||
|
|
@ -432,7 +533,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);
|
||||
|
|
@ -444,6 +545,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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue