2024-04-10 12:47:10

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 00/16] power: sequencing: implement the subsystem and add first users

From: Bartosz Golaszewski <[email protected]>

Problem statement #1: Dynamic bus chicken-and-egg problem.

Certain on-board PCI devices need to be powered up before they are can be
detected but their PCI drivers won't get bound until the device is
powered-up so enabling the relevant resources in the PCI device driver
itself is impossible.

Problem statement #2: Sharing inter-dependent resources between devices.

Certain devices that use separate drivers (often on different busses)
share resources (regulators, clocks, etc.). Typically these resources
are reference-counted but in some cases there are additional interactions
between them to consider, for example specific power-up sequence timings.

===

The reason for tackling both of these problems in a single series is the
fact the the platform I'm working on - Qualcomm RB5 - deals with both and
both need to be addressed in order to enable WLAN and Bluetooth support
upstream.

The on-board WLAN/BT package - QCA6391 - has a Power Management Unit that
takes inputs from the host and exposes LDO outputs consumed by the BT and
WLAN modules which can be powered-up and down independently. However
a delay of 100ms must be respected between enabling the BT- and
WLAN-enable GPIOs.

A similar design with a discreet PMU is also employed in other models of
the WCN family of chips although we can often do without the delays. With
this series we add support for the WCN7850 as well.

===

We introduce a new subsystem here - the power sequencing framework. The
qcom-wcn driver that we add is its first user. It implements the power-up
sequences for QCA6390 and WCN7850 chips. However - we only use it to
power-up the bluetooth module in the former. We use it to driver the WLAN
modules in both. The reason for this is that for WCN7850 we have
comprehensive bindings already upstream together with existing DT users.
Porting them to using the pwrseq subsystem can be done separately and in
an incremental manner once the subsystem itself is upstream. We will also
have to ensure backward DT compatibility. To avoid overcomplicating this
series, let's leave it out for now.

===

This series is logically split into several sections. I'll go
patch-by-patch and explain each step.

Patches 1/16-5/16:

These contain all relevant DT bindings changes. We add new documents for
the QCA6390 & WCN7850 PMUs and ATH12K devices as well as extend the bindings
for the Qualcomm Bluetooth and ATH11K modules with regulators used by them
in QCA6390.

Patches 6/16-8/16:

These contain changes to device-tree sources for the three platforms we
work with in this series. We model the PMUs of the WLAN/BT chips as
top-level platform devices on the device tree. In order to limit the scope
of this series and not introduce an excessive amount of confusion with
deprecating DT bindings, we leave the Bluetooth nodes on sm8650 and sm8550
as is (meaning: they continue to consumer the GPIOs and power inputs from
the host). As the WCN7850 module doesn't require any specific timings, we can
incrementally change that later.

In both cases we add WLAN nodes that consume the power outputs of the PMU.
For QCA6390 we also make the Bluetooth node of the RB5 consume the outputs
of the PMU - we can do it as the bindings for this chip did not define any
supply handles prior to this series meaning we are able to get this correct
right away.

Patches 9/16-12/16:

These contain the bulk of the PCI changes for this series. We introduce
a simple framework for powering up PCI devices before detecting them on
the bus.

The general approach is as follows: PCI devices that need special
treatment before they can be powered up, scanned and bound to their PCI
drivers must be described on the device-tree as child nodes of the PCI
port node. These devices will be instantiated on the platform bus. They
will in fact be generic platform devices with the compatible of the form
used for PCI devices already upstream ("pci<vendor ID>,<device ID">). We
add a new directory under drivers/pci/pwrctl/ that contains PCI pwrctl
drivers. These drivers are platform drivers that will now be matched
against the devices instantiated from port children just like any other
platform pairs.

Both the power control platform device *AND* the associated PCI device
reuse the same OF node and have access to the same properties. The goal
of the platform driver is to request and bring up any required resources
and let the pwrctl framework know that it's now OK to rescan the bus and
detect the devices. When the device is bound, we are notified about it
by the PCI bus notifier event and can establish a device link between the
power control device and the PCI device so that any future extension for
power-management will already be able to work with the correct hierachy.

The reusing of the OF node is the reason for the small changes to the PCI
OF core: as the bootloader can possibly leave the relevant regulators on
before booting linux, the PCI device can be detected before its platform
abstraction is probed. In this case, we find that device first and mark
its OF node as reused. The pwrctl framework handles the opposite case
(when the PCI device is detected only after the platform driver
successfully enabled it).

Patch 13/16 - 14/16:

These add a relatively simple power sequencing subsystem and the first
driver using it: the pwrseq module for the PMUs on the WCN family of chips.

I'm proposing to add a subsystem that allows different devices to use a shared
power sequence split into consumer-specific as well as common "units".

A power sequence provider driver registers a set of units with pwrseq
core. Each unit can be enabled and disabled and contains an optional list
of other units which must be enabled before it itself can be. A unit
represents a discreet chunk of the power sequence.

It also registers a list of targets: a target is an abstraction wrapping
a unit which allows consumers to tell pwrseq which unit they want to
reach. Real-life example is the driver we're adding here: there's a set
of common regulators, two PCIe-specific ones and two enable GPIOs: one
for Bluetooth and one for WLAN.

The Bluetooth driver requests a descriptor to the power sequencer and
names the target it wants to reach:

pwrseq = devm_pwrseq_get(dev, "bluetooth");

The pwrseq core then knows that when the driver calls:

pwrseq_power_on(pwrseq);

It must enable the "bluetooth-enable" unit but it depends on the
"regulators-common" unit so this one is enabled first. The provider
driver is also in charge of assuring an appropriate delay between
enabling the BT and WLAN enable GPIOs. The WLAN-specific resources are
handled by the "wlan-enable" unit and so are not enabled until the WLAN
driver requests the "wlan" target to be powered on.

Another thing worth discussing is the way we associate the consumer with
the relevant power sequencer. DT maintainers have expressed a discontent
with the existing mmc pwrseq bindings and have NAKed an earlier
initiative to introduce global pwrseq bindings to the kernel[1].

In this approach, we model the existing regulators and GPIOs in DT but
the pwrseq subsystem requires each provider to provide a .match()
callback. Whenever a consumer requests a power sequencer handle, we
iterate over the list of pwrseq drivers and call .match() for each. It's
up to the driver to verify in a platform-specific way whether it deals
with its consumer and let the core pwrseq code know.

The advantage of this over reusing the regulator or reset subsystem is
that it's more generalized and can handle resources of all kinds as well
as deal with any kind of power-on sequences: for instance, Qualcomm has
a PCI switch they want a driver for but this switch requires enabling
some resources first (PCI pwrctl) and then configuring the device over
I2C (which can be handled by the pwrseq provider).

Patch 15:

This patch makes the Qualcomm Bluetooth driver get and use the power
sequencer for QCA6390.

Patch 16:

While tiny, this patch is possibly the highlight of the entire series.
It uses the two abstraction layers we introduced before to create an
elegant power sequencing PCI power control driver and supports the ath11k
module on QCA6390 and ath12k on WCN7850.

With this series we can now enable BT and WLAN on several new Qualcomm
boards upstream.

Tested on RB5, sm8650-qrd and sm8550-qrd.

Changelog:

Since v6:
- kernel doc fixes
- drop myself from the DT bindings maintainers list for ath12k
- wait until the PCI bridge device is fully added before creating the
PCI pwrctl platform devices for its sub-nodes, otherwise we may see
sysfs and procfs attribute failures (due to duplication, we're
basically trying to probe the same device twice at the same time)
- I kept the regulators for QCA6390's ath11k as required as they only
apply to this specific Qualcomm package

Since v5:
- unify the approach to modelling the WCN WLAN/BT chips by always exposing
the PMU node on the device tree and making the WLAN and BT nodes become
consumers of its power outputs; this includes a major rework of the DT
sources, bindings and driver code; there's no more a separate PCI
pwrctl driver for WCN7850, instead its power-up sequence was moved
into the pwrseq driver common for all WCN chips
- don't set load_uA from new regulator consumers
- fix reported kerneldoc issues
- drop voltage ranges for PMU outputs from DT
- many minor tweaks and reworks

v1: Original RFC:

https://lore.kernel.org/lkml/[email protected]/T/

v2: First real patch series (should have been PATCH v2) adding what I
referred to back then as PCI power sequencing:

https://lore.kernel.org/linux-arm-kernel/2024021413-grumbling-unlivable-c145@gregkh/T/

v3: RFC for the DT representation of the PMU supplying the WLAN and BT
modules inside the QCA6391 package (was largely separate from the
series but probably should have been called PATCH or RFC v3):

https://lore.kernel.org/all/CAMRc=Mc+GNoi57eTQg71DXkQKjdaoAmCpB=h2ndEpGnmdhVV-Q@mail.gmail.com/T/

v4: Second attempt at the full series with changed scope (introduction of
the pwrseq subsystem, should have been RFC v4)

https://lore.kernel.org/lkml/[email protected]/T/

v5: Two different ways of handling QCA6390 and WCN7850:

https://lore.kernel.org/lkml/[email protected]/

Bartosz Golaszewski (16):
regulator: dt-bindings: describe the PMU module of the QCA6390 package
regulator: dt-bindings: describe the PMU module of the WCN7850 package
dt-bindings: net: bluetooth: qualcomm: describe regulators for QCA6390
dt-bindings: net: wireless: qcom,ath11k: describe the ath11k on
QCA6390
dt-bindings: net: wireless: describe the ath12k PCI module
arm64: dts: qcom: sm8550-qrd: add the Wifi node
arm64: dts: qcom: sm8650-qrd: add the Wifi node
arm64: dts: qcom: qrb5165-rb5: add the Wifi node
PCI: hold the rescan mutex when scanning for the first time
PCI/pwrctl: reuse the OF node for power controlled devices
PCI/pwrctl: create platform devices for child OF nodes of the port
node
PCI/pwrctl: add PCI power control core code
power: sequencing: implement the pwrseq core
power: pwrseq: add a driver for the PMU module on the QCom WCN
chipsets
Bluetooth: qca: use the power sequencer for QCA6390
PCI/pwrctl: add a PCI power control driver for power sequenced devices

.../net/bluetooth/qualcomm-bluetooth.yaml | 17 +
.../net/wireless/qcom,ath11k-pci.yaml | 46 +
.../bindings/net/wireless/qcom,ath12k.yaml | 99 ++
.../bindings/regulator/qcom,qca6390-pmu.yaml | 185 +++
MAINTAINERS | 8 +
arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 103 +-
arch/arm64/boot/dts/qcom/sm8250.dtsi | 10 +
arch/arm64/boot/dts/qcom/sm8550-qrd.dts | 97 ++
arch/arm64/boot/dts/qcom/sm8550.dtsi | 10 +
arch/arm64/boot/dts/qcom/sm8650-qrd.dts | 89 ++
arch/arm64/boot/dts/qcom/sm8650.dtsi | 10 +
drivers/bluetooth/hci_qca.c | 74 +-
drivers/pci/Kconfig | 1 +
drivers/pci/Makefile | 1 +
drivers/pci/bus.c | 9 +
drivers/pci/of.c | 14 +-
drivers/pci/probe.c | 2 +
drivers/pci/pwrctl/Kconfig | 17 +
drivers/pci/pwrctl/Makefile | 6 +
drivers/pci/pwrctl/core.c | 137 +++
drivers/pci/pwrctl/pci-pwrctl-pwrseq.c | 89 ++
drivers/pci/remove.c | 2 +
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/sequencing/Kconfig | 28 +
drivers/power/sequencing/Makefile | 6 +
drivers/power/sequencing/core.c | 1065 +++++++++++++++++
drivers/power/sequencing/pwrseq-qcom-wcn.c | 336 ++++++
include/linux/pci-pwrctl.h | 51 +
include/linux/pwrseq/consumer.h | 56 +
include/linux/pwrseq/provider.h | 75 ++
31 files changed, 2615 insertions(+), 30 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml
create mode 100644 Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
create mode 100644 drivers/pci/pwrctl/Kconfig
create mode 100644 drivers/pci/pwrctl/Makefile
create mode 100644 drivers/pci/pwrctl/core.c
create mode 100644 drivers/pci/pwrctl/pci-pwrctl-pwrseq.c
create mode 100644 drivers/power/sequencing/Kconfig
create mode 100644 drivers/power/sequencing/Makefile
create mode 100644 drivers/power/sequencing/core.c
create mode 100644 drivers/power/sequencing/pwrseq-qcom-wcn.c
create mode 100644 include/linux/pci-pwrctl.h
create mode 100644 include/linux/pwrseq/consumer.h
create mode 100644 include/linux/pwrseq/provider.h

--
2.40.1



2024-04-10 12:49:07

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 04/16] dt-bindings: net: wireless: qcom,ath11k: describe the ath11k on QCA6390

From: Bartosz Golaszewski <[email protected]>

Add a PCI compatible for the ATH11K module on QCA6390 and describe the
power inputs from the PMU that it consumes.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
.../net/wireless/qcom,ath11k-pci.yaml | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml
index 41d023797d7d..8675d7d0215c 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml
@@ -17,6 +17,7 @@ description: |
properties:
compatible:
enum:
+ - pci17cb,1101 # QCA6390
- pci17cb,1103 # WCN6855

reg:
@@ -28,10 +29,55 @@ properties:
string to uniquely identify variant of the calibration data for designs
with colliding bus and device ids

+ vddrfacmn-supply:
+ description: VDD_RFA_CMN supply regulator handle
+
+ vddaon-supply:
+ description: VDD_AON supply regulator handle
+
+ vddwlcx-supply:
+ description: VDD_WL_CX supply regulator handle
+
+ vddwlmx-supply:
+ description: VDD_WL_MX supply regulator handle
+
+ vddrfa0p8-supply:
+ description: VDD_RFA_0P8 supply regulator handle
+
+ vddrfa1p2-supply:
+ description: VDD_RFA_1P2 supply regulator handle
+
+ vddrfa1p7-supply:
+ description: VDD_RFA_1P7 supply regulator handle
+
+ vddpcie0p9-supply:
+ description: VDD_PCIE_0P9 supply regulator handle
+
+ vddpcie1p8-supply:
+ description: VDD_PCIE_1P8 supply regulator handle
+
required:
- compatible
- reg

+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: pci17cb,1101
+ then:
+ required:
+ - vddrfacmn-supply
+ - vddaon-supply
+ - vddwlcx-supply
+ - vddwlmx-supply
+ - vddrfa0p8-supply
+ - vddrfa1p2-supply
+ - vddrfa1p7-supply
+ - vddpcie0p9-supply
+ - vddpcie1p8-supply
+
additionalProperties: false

examples:
--
2.40.1


2024-04-10 12:49:07

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 05/16] dt-bindings: net: wireless: describe the ath12k PCI module

From: Bartosz Golaszewski <[email protected]>

Add device-tree bindings for the ATH12K module found in the WCN7850
package.

Signed-off-by: Bartosz Golaszewski <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
.../bindings/net/wireless/qcom,ath12k.yaml | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml

diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml
new file mode 100644
index 000000000000..f04d72d2c88e
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k.yaml
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2024 Linaro Limited
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/wireless/qcom,ath12k.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies ath12k wireless devices (PCIe)
+
+maintainers:
+ - Jeff Johnson <[email protected]>
+ - Kalle Valo <[email protected]>
+
+description:
+ Qualcomm Technologies IEEE 802.11ax PCIe devices.
+
+properties:
+ compatible:
+ enum:
+ - pci17cb,1107 # WCN7850
+
+ reg:
+ maxItems: 1
+
+ vddaon-supply:
+ description: VDD_AON supply regulator handle
+
+ vddwlcx-supply:
+ description: VDD_WLCX supply regulator handle
+
+ vddwlmx-supply:
+ description: VDD_WLMX supply regulator handle
+
+ vddrfacmn-supply:
+ description: VDD_RFA_CMN supply regulator handle
+
+ vddrfa0p8-supply:
+ description: VDD_RFA_0P8 supply regulator handle
+
+ vddrfa1p2-supply:
+ description: VDD_RFA_1P2 supply regulator handle
+
+ vddrfa1p8-supply:
+ description: VDD_RFA_1P8 supply regulator handle
+
+ vddpcie0p9-supply:
+ description: VDD_PCIE_0P9 supply regulator handle
+
+ vddpcie1p8-supply:
+ description: VDD_PCIE_1P8 supply regulator handle
+
+required:
+ - compatible
+ - reg
+ - vddaon-supply
+ - vddwlcx-supply
+ - vddwlmx-supply
+ - vddrfacmn-supply
+ - vddrfa0p8-supply
+ - vddrfa1p2-supply
+ - vddrfa1p8-supply
+ - vddpcie0p9-supply
+ - vddpcie1p8-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,rpmh.h>
+ #include <dt-bindings/gpio/gpio.h>
+ pcie {
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ bus-range = <0x01 0xff>;
+
+ wifi@0 {
+ compatible = "pci17cb,1107";
+ reg = <0x10000 0x0 0x0 0x0 0x0>;
+
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
+ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>;
+ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>;
+ };
+ };
+ };
--
2.40.1


2024-04-10 12:50:04

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 06/16] arm64: dts: qcom: sm8550-qrd: add the Wifi node

From: Bartosz Golaszewski <[email protected]>

Describe the ath12k WLAN on-board the WCN7850 module present on the
board.

[Neil: authored the initial version of the change]
Co-developed-by: Neil Armstrong <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
Signed-off-by: Bartosz Golaszewski <[email protected]>
---
arch/arm64/boot/dts/qcom/sm8550-qrd.dts | 97 +++++++++++++++++++++++++
arch/arm64/boot/dts/qcom/sm8550.dtsi | 10 +++
2 files changed, 107 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm8550-qrd.dts b/arch/arm64/boot/dts/qcom/sm8550-qrd.dts
index 92f015017418..caac40a799a5 100644
--- a/arch/arm64/boot/dts/qcom/sm8550-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/sm8550-qrd.dts
@@ -214,6 +214,68 @@ vph_pwr: vph-pwr-regulator {
regulator-always-on;
regulator-boot-on;
};
+
+ wcn7850-pmu {
+ compatible = "qcom,wcn7850-pmu";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_en>, <&pmk8550_sleep_clk>;
+
+ wlan-enable-gpios = <&tlmm 80 GPIO_ACTIVE_HIGH>;
+ /*
+ * TODO Add bt-enable-gpios once the Bluetooth driver is
+ * converted to using the power sequencer.
+ */
+
+ vdd-supply = <&vreg_s5g_0p85>;
+ vddio-supply = <&vreg_l15b_1p8>;
+ vddaon-supply = <&vreg_s2g_0p85>;
+ vdddig-supply = <&vreg_s4e_0p95>;
+ vddrfa1p2-supply = <&vreg_s4g_1p25>;
+ vddrfa1p8-supply = <&vreg_s6g_1p86>;
+
+ regulators {
+ vreg_pmu_rfa_cmn: ldo0 {
+ regulator-name = "vreg_pmu_rfa_cmn";
+ };
+
+ vreg_pmu_aon_0p59: ldo1 {
+ regulator-name = "vreg_pmu_aon_0p59";
+ };
+
+ vreg_pmu_wlcx_0p8: ldo2 {
+ regulator-name = "vreg_pmu_wlcx_0p8";
+ };
+
+ vreg_pmu_wlmx_0p85: ldo3 {
+ regulator-name = "vreg_pmu_wlmx_0p85";
+ };
+
+ vreg_pmu_btcmx_0p85: ldo4 {
+ regulator-name = "vreg_pmu_btcmx_0p85";
+ };
+
+ vreg_pmu_rfa_0p8: ldo5 {
+ regulator-name = "vreg_pmu_rfa_0p8";
+ };
+
+ vreg_pmu_rfa_1p2: ldo6 {
+ regulator-name = "vreg_pmu_rfa_1p2";
+ };
+
+ vreg_pmu_rfa_1p8: ldo7 {
+ regulator-name = "vreg_pmu_rfa_1p8";
+ };
+
+ vreg_pmu_pcie_0p9: ldo8 {
+ regulator-name = "vreg_pmu_pcie_0p9";
+ };
+
+ vreg_pmu_pcie_1p8: ldo9 {
+ regulator-name = "vreg_pmu_pcie_1p8";
+ };
+ };
+ };
};

&apps_rsc {
@@ -824,6 +886,23 @@ &pcie0 {
status = "okay";
};

+&pcieport0 {
+ wifi@0 {
+ compatible = "pci17cb,1107";
+ reg = <0x10000 0x0 0x0 0x0 0x0>;
+
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
+ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>;
+ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>;
+ };
+};
+
&pcie0_phy {
vdda-phy-supply = <&vreg_l1e_0p88>;
vdda-pll-supply = <&vreg_l3e_1p2>;
@@ -911,6 +990,17 @@ &pcie_1_phy_aux_clk {
clock-frequency = <1000>;
};

+&pmk8550_gpios {
+ pmk8550_sleep_clk: sleep-clk-state {
+ pins = "gpio3";
+ function = "func1";
+ input-disable;
+ output-enable;
+ bias-disable;
+ power-source = <0>;
+ };
+};
+
&qupv3_id_0 {
status = "okay";
};
@@ -1084,6 +1174,13 @@ wcd_default: wcd-reset-n-active-state {
bias-disable;
output-low;
};
+
+ wlan_en: wlan-en-state {
+ pins = "gpio80";
+ function = "gpio";
+ drive-strength = <8>;
+ bias-pull-down;
+ };
};

&uart7 {
diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi
index 5cae8d773cec..f09406fd0ca6 100644
--- a/arch/arm64/boot/dts/qcom/sm8550.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi
@@ -1770,6 +1770,16 @@ pcie0: pcie@1c00000 {
phy-names = "pciephy";

status = "disabled";
+
+ pcieport0: pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ bus-range = <0x01 0xff>;
+ };
};

pcie0_phy: phy@1c06000 {
--
2.40.1


2024-04-10 12:52:28

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 11/16] PCI/pwrctl: create platform devices for child OF nodes of the port node

From: Bartosz Golaszewski <[email protected]>

In preparation for introducing PCI device power control - a set of
library functions that will allow powering-up of PCI devices before
they're detected on the PCI bus - we need to populate the devices
defined on the device-tree.

We are reusing the platform bus as it provides us with all the
infrastructure we need to match the pwrctl drivers against the
compatibles from OF nodes.

These platform devices will be probed by the driver core and bound to
the PCI pwrctl drivers we'll introduce later.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
drivers/pci/bus.c | 9 +++++++++
drivers/pci/remove.c | 2 ++
2 files changed, 11 insertions(+)

diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 826b5016a101..3e3517567721 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>

@@ -354,6 +355,14 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_warn(dev, "device attach failed (%d)\n", retval);

pci_dev_assign_added(dev, true);
+
+ if (pci_is_bridge(dev)) {
+ retval = of_platform_populate(dev->dev.of_node, NULL, NULL,
+ &dev->dev);
+ if (retval)
+ pci_err(dev, "failed to populate child OF nodes (%d)\n",
+ retval);
+ }
}
EXPORT_SYMBOL_GPL(pci_bus_add_device);

diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index d749ea8250d6..fc9db2805888 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
#include <linux/module.h>
+#include <linux/of_platform.h>
#include "pci.h"

static void pci_free_resources(struct pci_dev *dev)
@@ -22,6 +23,7 @@ static void pci_stop_dev(struct pci_dev *dev)
device_release_driver(&dev->dev);
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
+ of_platform_depopulate(&dev->dev);
of_pci_remove_node(dev);

pci_dev_assign_added(dev, false);
--
2.40.1


2024-04-10 12:52:45

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 12/16] PCI/pwrctl: add PCI power control core code

From: Bartosz Golaszewski <[email protected]>

Some PCI devices must be powered-on before they can be detected on the
bus. Introduce a simple framework reusing the existing PCI OF
infrastructure.

The way this works is: a DT node representing a PCI device connected to
the port can be matched against its power control platform driver. If
the match succeeds, the driver is responsible for powering-up the device
and calling pcie_pwrctl_device_set_ready() which will trigger a PCI bus
rescan as well as subscribe to PCI bus notifications.

When the device is detected and created, we'll make it consume the same
DT node that the platform device did. When the device is bound, we'll
create a device link between it and the parent power control device.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
drivers/pci/Kconfig | 1 +
drivers/pci/Makefile | 1 +
drivers/pci/pwrctl/Kconfig | 8 +++
drivers/pci/pwrctl/Makefile | 4 ++
drivers/pci/pwrctl/core.c | 137 ++++++++++++++++++++++++++++++++++++
include/linux/pci-pwrctl.h | 51 ++++++++++++++
6 files changed, 202 insertions(+)
create mode 100644 drivers/pci/pwrctl/Kconfig
create mode 100644 drivers/pci/pwrctl/Makefile
create mode 100644 drivers/pci/pwrctl/core.c
create mode 100644 include/linux/pci-pwrctl.h

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index d35001589d88..aa4d1833f442 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -296,5 +296,6 @@ source "drivers/pci/hotplug/Kconfig"
source "drivers/pci/controller/Kconfig"
source "drivers/pci/endpoint/Kconfig"
source "drivers/pci/switch/Kconfig"
+source "drivers/pci/pwrctl/Kconfig"

endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 175302036890..8ddad57934a6 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \

obj-$(CONFIG_PCI) += msi/
obj-$(CONFIG_PCI) += pcie/
+obj-$(CONFIG_PCI) += pwrctl/

ifdef CONFIG_PCI
obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/drivers/pci/pwrctl/Kconfig b/drivers/pci/pwrctl/Kconfig
new file mode 100644
index 000000000000..96195395af69
--- /dev/null
+++ b/drivers/pci/pwrctl/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "PCI Power control drivers"
+
+config PCI_PWRCTL
+ tristate
+
+endmenu
diff --git a/drivers/pci/pwrctl/Makefile b/drivers/pci/pwrctl/Makefile
new file mode 100644
index 000000000000..52ae0640ef7b
--- /dev/null
+++ b/drivers/pci/pwrctl/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_PCI_PWRCTL) += pci-pwrctl-core.o
+pci-pwrctl-core-y := core.o
diff --git a/drivers/pci/pwrctl/core.c b/drivers/pci/pwrctl/core.c
new file mode 100644
index 000000000000..332e16003505
--- /dev/null
+++ b/drivers/pci/pwrctl/core.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Linaro Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci-pwrctl.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+static int pci_pwrctl_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct pci_pwrctl *pwrctl = container_of(nb, struct pci_pwrctl, nb);
+ struct device *dev = data;
+
+ if (dev_fwnode(dev) != dev_fwnode(pwrctl->dev))
+ return NOTIFY_DONE;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ /*
+ * We will have two struct device objects bound to two different
+ * drivers on different buses but consuming the same DT node. We
+ * must not bind the pins twice in this case but only once for
+ * the first device to be added.
+ *
+ * If we got here then the PCI device is the second after the
+ * power control platform device. Mark its OF node as reused.
+ */
+ dev->of_node_reused = true;
+ break;
+ case BUS_NOTIFY_BOUND_DRIVER:
+ pwrctl->link = device_link_add(dev, pwrctl->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ if (!pwrctl->link)
+ dev_err(pwrctl->dev, "Failed to add device link\n");
+ break;
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ if (pwrctl->link)
+ device_link_del(pwrctl->link);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * pci_pwrctl_device_set_ready() - Notify the pwrctl subsystem that the PCI
+ * device is powered-up and ready to be detected.
+ *
+ * @pwrctl: PCI power control data.
+ *
+ * Returns:
+ * 0 on success, negative error number on error.
+ *
+ * Note:
+ * This function returning 0 doesn't mean the device was detected. It means,
+ * that the bus rescan was successfully started. The device will get bound to
+ * its PCI driver asynchronously.
+ */
+int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl)
+{
+ int ret;
+
+ if (!pwrctl->dev)
+ return -ENODEV;
+
+ pwrctl->nb.notifier_call = pci_pwrctl_notify;
+ ret = bus_register_notifier(&pci_bus_type, &pwrctl->nb);
+ if (ret)
+ return ret;
+
+ pci_lock_rescan_remove();
+ pci_rescan_bus(to_pci_dev(pwrctl->dev->parent)->bus);
+ pci_unlock_rescan_remove();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_pwrctl_device_set_ready);
+
+/**
+ * pci_pwrctl_device_unset_ready() - Notify the pwrctl subsystem that the PCI
+ * device is about to be powered-down.
+ *
+ * @pwrctl: PCI power control data.
+ */
+void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl)
+{
+ /*
+ * We don't have to delete the link here. Typically, this function
+ * is only called when the power control device is being detached. If
+ * it is being detached then the child PCI device must have already
+ * been unbound too or the device core wouldn't let us unbind.
+ */
+ bus_unregister_notifier(&pci_bus_type, &pwrctl->nb);
+}
+EXPORT_SYMBOL_GPL(pci_pwrctl_device_unset_ready);
+
+static void devm_pci_pwrctl_device_unset_ready(void *data)
+{
+ struct pci_pwrctl *pwrctl = data;
+
+ pci_pwrctl_device_set_ready(pwrctl);
+}
+
+/**
+ * devm_pci_pwrctl_device_set_ready - Managed variant of
+ * pci_pwrctl_device_set_ready().
+ *
+ * @dev: Device managing this pwrctl provider.
+ * @pwrctl: PCI power control data.
+ *
+ * Returns:
+ * 0 on success, negative error number on error.
+ */
+int devm_pci_pwrctl_device_set_ready(struct device *dev,
+ struct pci_pwrctl *pwrctl)
+{
+ int ret;
+
+ ret = pci_pwrctl_device_set_ready(pwrctl);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev,
+ devm_pci_pwrctl_device_unset_ready,
+ pwrctl);
+}
+EXPORT_SYMBOL_GPL(devm_pci_pwrctl_device_set_ready);
+
+MODULE_AUTHOR("Bartosz Golaszewski <[email protected]>");
+MODULE_DESCRIPTION("PCI Device Power Control core driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/pci-pwrctl.h b/include/linux/pci-pwrctl.h
new file mode 100644
index 000000000000..45e9cfe740e4
--- /dev/null
+++ b/include/linux/pci-pwrctl.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Linaro Ltd.
+ */
+
+#ifndef __PCI_PWRCTL_H__
+#define __PCI_PWRCTL_H__
+
+#include <linux/notifier.h>
+
+struct device;
+struct device_link;
+
+/*
+ * This is a simple framework for solving the issue of PCI devices that require
+ * certain resources (regulators, GPIOs, clocks) to be enabled before the
+ * device can actually be detected on the PCI bus.
+ *
+ * The idea is to reuse the platform bus to populate OF nodes describing the
+ * PCI device and its resources, let these platform devices probe and enable
+ * relevant resources and then trigger a rescan of the PCI bus allowing for the
+ * same device (with a second associated struct device) to be registered with
+ * the PCI subsystem.
+ *
+ * To preserve a correct hierarchy for PCI power management and device reset,
+ * we create a device link between the power control platform device (parent)
+ * and the supplied PCI device (child).
+ */
+
+/**
+ * struct pci_pwrctl - PCI device power control context.
+ * @dev: Address of the power controlling device.
+ *
+ * An object of this type must be allocated by the PCI power control device and
+ * passed to the pwrctl subsystem to trigger a bus rescan and setup a device
+ * link with the device once it's up.
+ */
+struct pci_pwrctl {
+ struct device *dev;
+
+ /* Private: don't use. */
+ struct notifier_block nb;
+ struct device_link *link;
+};
+
+int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl);
+void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl);
+int devm_pci_pwrctl_device_set_ready(struct device *dev,
+ struct pci_pwrctl *pwrctl);
+
+#endif /* __PCI_PWRCTL_H__ */
--
2.40.1


2024-04-10 12:54:07

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 14/16] power: pwrseq: add a driver for the PMU module on the QCom WCN chipsets

From: Bartosz Golaszewski <[email protected]>

This adds the power sequencing driver for the PMU modules present on the
Qualcomm WCN Bluetooth and Wifi chipsets. It uses the pwrseq subsystem
and knows how to match the sequencer to the consumer device by verifying
the relevant properties and DT layout.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
drivers/power/sequencing/Kconfig | 16 +
drivers/power/sequencing/Makefile | 2 +
drivers/power/sequencing/pwrseq-qcom-wcn.c | 336 +++++++++++++++++++++
3 files changed, 354 insertions(+)
create mode 100644 drivers/power/sequencing/pwrseq-qcom-wcn.c

diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig
index ba5732b1dbf8..468d58fac07d 100644
--- a/drivers/power/sequencing/Kconfig
+++ b/drivers/power/sequencing/Kconfig
@@ -10,3 +10,19 @@ menuconfig POWER_SEQUENCING
during power-up.

If unsure, say no.
+
+if POWER_SEQUENCING
+
+config POWER_SEQUENCING_QCOM_WCN
+ tristate "Qualcomm WCN family PMU driver"
+ default m if ARCH_QCOM
+ help
+ Say U here to enable the power sequencing driver for Qualcomm
+ WCN Bluetooth/WLAN chipsets.
+
+ Typically, a package from the Qualcomm WCN family contains the BT
+ and WLAN modules whose power is controlled by the PMU module. As the
+ former two share the power-up sequence which is executed by the PMU,
+ this driver is needed for correct power control.
+
+endif
diff --git a/drivers/power/sequencing/Makefile b/drivers/power/sequencing/Makefile
index dcdf8c0c159e..2eec2df7912d 100644
--- a/drivers/power/sequencing/Makefile
+++ b/drivers/power/sequencing/Makefile
@@ -2,3 +2,5 @@

obj-$(CONFIG_POWER_SEQUENCING) += pwrseq-core.o
pwrseq-core-y := core.o
+
+obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o
diff --git a/drivers/power/sequencing/pwrseq-qcom-wcn.c b/drivers/power/sequencing/pwrseq-qcom-wcn.c
new file mode 100644
index 000000000000..380305227b3f
--- /dev/null
+++ b/drivers/power/sequencing/pwrseq-qcom-wcn.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Linaro Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwrseq/provider.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+struct pwrseq_qcom_wcn_pdata {
+ const char *const *vregs;
+ size_t num_vregs;
+ unsigned int pwup_delay_msec;
+ unsigned int gpio_enable_delay;
+};
+
+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 gpio_desc *bt_gpio;
+ struct gpio_desc *wlan_gpio;
+ struct clk *clk;
+ unsigned long last_gpio_enable;
+};
+
+static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx)
+{
+ unsigned long diff_jiffies;
+ unsigned int diff_msecs;
+
+ if (!ctx->pdata->gpio_enable_delay)
+ return;
+
+ diff_jiffies = jiffies - ctx->last_gpio_enable;
+ diff_msecs = jiffies_to_msecs(diff_jiffies);
+
+ if (diff_msecs < ctx->pdata->gpio_enable_delay)
+ msleep(ctx->pdata->gpio_enable_delay - diff_msecs);
+}
+
+static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ return regulator_bulk_enable(ctx->pdata->num_vregs, ctx->regs);
+}
+
+static int pwrseq_qcom_wcn_vregs_disable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ return regulator_bulk_disable(ctx->pdata->num_vregs, ctx->regs);
+}
+
+static const struct pwrseq_unit_data pwrseq_qcom_wcn_vregs_unit_data = {
+ .name = "regulators-enable",
+ .enable = pwrseq_qcom_wcn_vregs_enable,
+ .disable = pwrseq_qcom_wcn_vregs_disable,
+};
+
+static int pwrseq_qcom_wcn_clk_enable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ return clk_prepare_enable(ctx->clk);
+}
+
+static int pwrseq_qcom_wcn_clk_disable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ clk_disable_unprepare(ctx->clk);
+
+ return 0;
+}
+
+static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = {
+ .name = "clock-enable",
+ .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,
+ NULL
+};
+
+static int pwrseq_qcom_wcn_bt_enable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ pwrseq_qcom_wcn_ensure_gpio_delay(ctx);
+ gpiod_set_value_cansleep(ctx->bt_gpio, 1);
+ ctx->last_gpio_enable = jiffies;
+
+ return 0;
+}
+
+static int pwrseq_qcom_wcn_bt_disable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ gpiod_set_value_cansleep(ctx->bt_gpio, 0);
+
+ return 0;
+}
+
+static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = {
+ .name = "bluetooth-enable",
+ .deps = pwrseq_qcom_wcn_unit_deps,
+ .enable = pwrseq_qcom_wcn_bt_enable,
+ .disable = pwrseq_qcom_wcn_bt_disable,
+};
+
+static int pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ pwrseq_qcom_wcn_ensure_gpio_delay(ctx);
+ gpiod_set_value_cansleep(ctx->wlan_gpio, 1);
+ ctx->last_gpio_enable = jiffies;
+
+ return 0;
+}
+
+static int pwrseq_qcom_wcn_wlan_disable(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ gpiod_set_value_cansleep(ctx->wlan_gpio, 0);
+
+ return 0;
+}
+
+static const struct pwrseq_unit_data pwrseq_qcom_wcn_wlan_unit_data = {
+ .name = "wlan-enable",
+ .deps = pwrseq_qcom_wcn_unit_deps,
+ .enable = pwrseq_qcom_wcn_wlan_enable,
+ .disable = pwrseq_qcom_wcn_wlan_disable,
+};
+
+static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+
+ if (ctx->pdata->pwup_delay_msec)
+ msleep(ctx->pdata->pwup_delay_msec);
+
+ return 0;
+}
+
+static const struct pwrseq_target_data pwrseq_qcom_wcn_bt_target_data = {
+ .name = "bluetooth",
+ .unit = &pwrseq_qcom_wcn_bt_unit_data,
+ .post_enable = pwrseq_qcom_wcn_pwup_delay,
+};
+
+static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = {
+ .name = "wlan",
+ .unit = &pwrseq_qcom_wcn_wlan_unit_data,
+ .post_enable = pwrseq_qcom_wcn_pwup_delay,
+};
+
+static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = {
+ &pwrseq_qcom_wcn_bt_target_data,
+ &pwrseq_qcom_wcn_wlan_target_data,
+ NULL
+};
+
+static const char *const pwrseq_qca6390_vregs[] = {
+ "vddio",
+ "vddaon",
+ "vddpmu",
+ "vddrfa0p95",
+ "vddrfa1p3",
+ "vddrfa1p9",
+ "vddpcie1p3",
+ "vddpcie1p9",
+};
+
+static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = {
+ .vregs = pwrseq_qca6390_vregs,
+ .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs),
+ .pwup_delay_msec = 60,
+ .gpio_enable_delay = 100,
+};
+
+static const char *const pwrseq_wcn7850_vregs[] = {
+ "vdd",
+ "vddio",
+ "vddio1p2",
+ "vddaon",
+ "vdddig",
+ "vddrfa1p2",
+ "vddrfa1p8",
+};
+
+static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = {
+ .vregs = pwrseq_wcn7850_vregs,
+ .num_vregs = ARRAY_SIZE(pwrseq_wcn7850_vregs),
+ .pwup_delay_msec = 50,
+};
+
+static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,
+ struct device *dev)
+{
+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
+ struct device_node *dev_node = dev->of_node;
+
+ /*
+ * The PMU supplies power to the Bluetooth and WLAN modules. both
+ * consume the PMU AON output so check the presence of the
+ * 'vddaon-supply' property and whether it leads us to the right
+ * device.
+ */
+ if (!of_property_present(dev_node, "vddaon-supply"))
+ return 0;
+
+ struct device_node *reg_node __free(device_node) =
+ of_parse_phandle(dev_node, "vddaon-supply", 0);
+ if (!reg_node)
+ return 0;
+
+ /*
+ * `reg_node` is the PMU AON regulator, its parent is the `regulators`
+ * node and finally its grandparent is the PMU device node that we're
+ * looking for.
+ */
+ if (!reg_node->parent || !reg_node->parent->parent ||
+ reg_node->parent->parent != ctx->of_node)
+ return 0;
+
+ return 1;
+}
+
+static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwrseq_qcom_wcn_ctx *ctx;
+ struct pwrseq_config config;
+ int i, ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->of_node = dev->of_node;
+
+ ctx->pdata = of_device_get_match_data(dev);
+ if (!ctx->pdata)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to obtain platform data\n");
+
+ ctx->regs = devm_kcalloc(dev, ctx->pdata->num_vregs,
+ sizeof(*ctx->regs), GFP_KERNEL);
+ if (!ctx->regs)
+ return -ENOMEM;
+
+ for (i = 0; i < ctx->pdata->num_vregs; i++)
+ ctx->regs[i].supply = ctx->pdata->vregs[i];
+
+ ret = devm_regulator_bulk_get(dev, ctx->pdata->num_vregs, ctx->regs);
+ if (ret < 0)
+ return dev_err_probe(dev, PTR_ERR(ctx->regs),
+ "Failed to get all regulators\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),
+ "Failed to get the Bluetooth enable GPIO\n");
+
+ ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->wlan_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio),
+ "Failed to get the WLAN enable GPIO\n");
+
+ ctx->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ctx->clk))
+ return dev_err_probe(dev, PTR_ERR(ctx->clk),
+ "Failed to get the reference clock\n");
+
+ memset(&config, 0, sizeof(config));
+
+ config.parent = dev;
+ config.owner = THIS_MODULE;
+ config.drvdata = ctx;
+ config.match = pwrseq_qcom_wcn_match;
+ config.targets = pwrseq_qcom_wcn_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_qcom_wcn_of_match[] = {
+ {
+ .compatible = "qcom,qca6390-pmu",
+ .data = &pwrseq_qca6390_of_data,
+ },
+ {
+ .compatible = "qcom,wcn7850-pmu",
+ .data = &pwrseq_wcn7850_of_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwrseq_qcom_wcn_of_match);
+
+static struct platform_driver pwrseq_qcom_wcn_driver = {
+ .driver = {
+ .name = "pwrseq-qcom_wcn",
+ .of_match_table = pwrseq_qcom_wcn_of_match,
+ },
+ .probe = pwrseq_qcom_wcn_probe,
+};
+module_platform_driver(pwrseq_qcom_wcn_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <[email protected]>");
+MODULE_DESCRIPTION("Qualcomm WCN PMU power sequencing driver");
+MODULE_LICENSE("GPL");
--
2.40.1


2024-04-10 12:54:25

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 15/16] Bluetooth: qca: use the power sequencer for QCA6390

From: Bartosz Golaszewski <[email protected]>

Use the pwrseq subsystem's consumer API to run the power-up sequence for
the Bluetooth module of the QCA6390 package.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
drivers/bluetooth/hci_qca.c | 74 +++++++++++++++++++++++++++++--------
1 file changed, 59 insertions(+), 15 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 8a60ad7acd70..d31dcea650c1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -29,6 +29,7 @@
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
#include <linux/mutex.h>
@@ -215,6 +216,7 @@ struct qca_power {
struct regulator_bulk_data *vreg_bulk;
int num_vregs;
bool vregs_on;
+ struct pwrseq_desc *pwrseq;
};

struct qca_serdev {
@@ -1682,6 +1684,27 @@ static bool qca_wakeup(struct hci_dev *hdev)
return wakeup;
}

+static int qca_port_reopen(struct hci_uart *hu)
+{
+ int ret;
+
+ /* Now the device is in ready state to communicate with host.
+ * To sync host with device we need to reopen port.
+ * Without this, we will have RTS and CTS synchronization
+ * issues.
+ */
+ serdev_device_close(hu->serdev);
+ ret = serdev_device_open(hu->serdev);
+ if (ret) {
+ bt_dev_err(hu->hdev, "failed to open port");
+ return ret;
+ }
+
+ hci_uart_set_flow_control(hu, false);
+
+ return 0;
+}
+
static int qca_regulator_init(struct hci_uart *hu)
{
enum qca_btsoc_type soc_type = qca_soc_type(hu);
@@ -1750,21 +1773,7 @@ static int qca_regulator_init(struct hci_uart *hu)
break;
}

- /* Now the device is in ready state to communicate with host.
- * To sync host with device we need to reopen port.
- * Without this, we will have RTS and CTS synchronization
- * issues.
- */
- serdev_device_close(hu->serdev);
- ret = serdev_device_open(hu->serdev);
- if (ret) {
- bt_dev_err(hu->hdev, "failed to open port");
- return ret;
- }
-
- hci_uart_set_flow_control(hu, false);
-
- return 0;
+ return qca_port_reopen(hu);
}

static int qca_power_on(struct hci_dev *hdev)
@@ -1792,6 +1801,17 @@ static int qca_power_on(struct hci_dev *hdev)
ret = qca_regulator_init(hu);
break;

+ case QCA_QCA6390:
+ qcadev = serdev_device_get_drvdata(hu->serdev);
+ ret = pwrseq_power_on(qcadev->bt_power->pwrseq);
+ if (ret)
+ return ret;
+
+ ret = qca_port_reopen(hu);
+ if (ret)
+ return ret;
+ break;
+
default:
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qcadev->bt_en) {
@@ -2170,6 +2190,10 @@ static void qca_power_shutdown(struct hci_uart *hu)
}
break;

+ case QCA_QCA6390:
+ pwrseq_power_off(qcadev->bt_power->pwrseq);
+ break;
+
default:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
}
@@ -2308,12 +2332,25 @@ static int qca_serdev_probe(struct serdev_device *serdev)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_QCA6390:
qcadev->bt_power = devm_kzalloc(&serdev->dev,
sizeof(struct qca_power),
GFP_KERNEL);
if (!qcadev->bt_power)
return -ENOMEM;
+ break;
+ default:
+ break;
+ }

+ switch (qcadev->btsoc_type) {
+ case QCA_WCN3988:
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+ case QCA_WCN6750:
+ case QCA_WCN6855:
+ case QCA_WCN7850:
qcadev->bt_power->dev = &serdev->dev;
err = qca_init_regulators(qcadev->bt_power, data->vregs,
data->num_vregs);
@@ -2354,6 +2391,13 @@ static int qca_serdev_probe(struct serdev_device *serdev)
}
break;

+ case QCA_QCA6390:
+ qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->dev,
+ "bluetooth");
+ if (IS_ERR(qcadev->bt_power->pwrseq))
+ return PTR_ERR(qcadev->bt_power->pwrseq);
+ fallthrough;
+
default:
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW);
--
2.40.1


2024-04-10 13:49:27

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 02/16] regulator: dt-bindings: describe the PMU module of the WCN7850 package

From: Bartosz Golaszewski <[email protected]>

The WCN7850 package contains discreet modules for WLAN and Bluetooth. They
are powered by the Power Management Unit (PMU) that takes inputs from the
host and provides LDO outputs. Extend the bindings for QCA6390 to also
document this model.

Signed-off-by: Bartosz Golaszewski <[email protected]>
Acked-by: Mark Brown <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
.../bindings/regulator/qcom,qca6390-pmu.yaml | 36 ++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml b/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
index 9d39ff9a75fd..2e543661a1e2 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
+++ b/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
@@ -16,20 +16,37 @@ description:

properties:
compatible:
- const: qcom,qca6390-pmu
+ enum:
+ - qcom,qca6390-pmu
+ - qcom,wcn7850-pmu
+
+ vdd-supply:
+ description: VDD supply regulator handle

vddaon-supply:
description: VDD_AON supply regulator handle

+ vdddig-supply:
+ description: VDD_DIG supply regulator handle
+
vddpmu-supply:
description: VDD_PMU supply regulator handle

+ vddio1p2-supply:
+ description: VDD_IO_1P2 supply regulator handle
+
vddrfa0p95-supply:
description: VDD_RFA_0P95 supply regulator handle

+ vddrfa1p2-supply:
+ description: VDD_RFA_1P2 supply regulator handle
+
vddrfa1p3-supply:
description: VDD_RFA_1P3 supply regulator handle

+ vddrfa1p8-supply:
+ description: VDD_RFA_1P8 supply regulator handle
+
vddrfa1p9-supply:
description: VDD_RFA_1P9 supply regulator handle

@@ -50,6 +67,10 @@ properties:
maxItems: 1
description: GPIO line enabling the ATH11K Bluetooth module supplied by the PMU

+ clocks:
+ maxItems: 1
+ description: Reference clock handle
+
regulators:
type: object
description:
@@ -83,6 +104,19 @@ allOf:
- vddpcie1p3-supply
- vddpcie1p9-supply
- vddio-supply
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,wcn7850-pmu
+ then:
+ required:
+ - vdd-supply
+ - vddio-supply
+ - vddaon-supply
+ - vdddig-supply
+ - vddrfa1p2-supply
+ - vddrfa1p8-supply

additionalProperties: false

--
2.40.1


2024-04-10 15:27:48

by Jeff Johnson

[permalink] [raw]
Subject: Re: [PATCH v7 05/16] dt-bindings: net: wireless: describe the ath12k PCI module

On 4/10/2024 5:46 AM, Bartosz Golaszewski wrote:
[...]
> +description:
> + Qualcomm Technologies IEEE 802.11ax PCIe devices.

if you respin, nit: s/11ax/11be/


2024-04-10 15:59:22

by Jeff Johnson

[permalink] [raw]
Subject: Re: [PATCH v7 14/16] power: pwrseq: add a driver for the PMU module on the QCom WCN chipsets

On 4/10/2024 5:46 AM, Bartosz Golaszewski wrote:
[...]
> +if POWER_SEQUENCING
> +
> +config POWER_SEQUENCING_QCOM_WCN
> + tristate "Qualcomm WCN family PMU driver"
> + default m if ARCH_QCOM
> + help
> + Say U here to enable the power sequencing driver for Qualcomm

did you mean: Say Y here?

> + WCN Bluetooth/WLAN chipsets.


2024-04-10 16:55:30

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 10/16] PCI/pwrctl: reuse the OF node for power controlled devices

From: Bartosz Golaszewski <[email protected]>

With PCI power control we deal with two struct device objects bound to
two different drivers but consuming the same OF node. We must not bind
the pinctrl twice. To that end: before setting the OF node of the newly
instantiated PCI device, check if a platform device consuming the same
OF node doesn't already exist on the platform bus and - if so - mark the
PCI device as reusing the OF node.

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
drivers/pci/of.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 51e3dd0ea5ab..b908fe1ae951 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -6,6 +6,7 @@
*/
#define pr_fmt(fmt) "PCI: OF: " fmt

+#include <linux/cleanup.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -13,6 +14,7 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
+#include <linux/platform_device.h>
#include "pci.h"

#ifdef CONFIG_PCI
@@ -25,16 +27,20 @@
*/
int pci_set_of_node(struct pci_dev *dev)
{
- struct device_node *node;
-
if (!dev->bus->dev.of_node)
return 0;

- node = of_pci_find_child_device(dev->bus->dev.of_node, dev->devfn);
+ struct device_node *node __free(device_node) =
+ of_pci_find_child_device(dev->bus->dev.of_node, dev->devfn);
if (!node)
return 0;

- device_set_node(&dev->dev, of_fwnode_handle(node));
+ struct device *pdev __free(put_device) =
+ bus_find_device_by_of_node(&platform_bus_type, node);
+ if (pdev)
+ dev->bus->dev.of_node_reused = true;
+
+ device_set_node(&dev->dev, of_fwnode_handle(no_free_ptr(node)));
return 0;
}

--
2.40.1


2024-04-10 19:03:23

by Bartosz Golaszewski

[permalink] [raw]
Subject: [PATCH v7 03/16] dt-bindings: net: bluetooth: qualcomm: describe regulators for QCA6390

From: Bartosz Golaszewski <[email protected]>

QCA6390 has a compatible listed in the bindings but is missing the
regulators description. Add the missing supply property and list the
required ones in the allOf section.

Signed-off-by: Bartosz Golaszewski <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
.../net/bluetooth/qualcomm-bluetooth.yaml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml
index 528ef3572b62..d844acaec1d3 100644
--- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml
+++ b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml
@@ -62,6 +62,9 @@ properties:
vdddig-supply:
description: VDD_DIG supply regulator handle

+ vddbtcmx-supply:
+ description: VDD_BT_CMX supply regulator handle
+
vddbtcxmx-supply:
description: VDD_BT_CXMX supply regulator handle

@@ -180,6 +183,20 @@ allOf:
- vddrfa0p8-supply
- vddrfa1p2-supply
- vddrfa1p9-supply
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,qca6390-bt
+ then:
+ required:
+ - vddrfacmn-supply
+ - vddaon-supply
+ - vddbtcmx-supply
+ - vddrfa0p8-supply
+ - vddrfa1p2-supply
+ - vddrfa1p7-supply

examples:
- |
--
2.40.1


2024-04-12 07:54:03

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v7 04/16] dt-bindings: net: wireless: qcom,ath11k: describe the ath11k on QCA6390

On 10/04/2024 14:46, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <[email protected]>
>
> Add a PCI compatible for the ATH11K module on QCA6390 and describe the
> power inputs from the PMU that it consumes.
>
> Signed-off-by: Bartosz Golaszewski <[email protected]>
> ---

Reviewed-by: Krzysztof Kozlowski <[email protected]>

Best regards,
Krzysztof


2024-04-14 10:44:31

by Simon Horman

[permalink] [raw]
Subject: Re: [PATCH v7 14/16] power: pwrseq: add a driver for the PMU module on the QCom WCN chipsets

On Wed, Apr 10, 2024 at 02:46:26PM +0200, Bartosz Golaszewski wrote:

...

> +static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct pwrseq_qcom_wcn_ctx *ctx;
> + struct pwrseq_config config;
> + int i, ret;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->of_node = dev->of_node;
> +
> + ctx->pdata = of_device_get_match_data(dev);
> + if (!ctx->pdata)
> + return dev_err_probe(dev, -ENODEV,
> + "Failed to obtain platform data\n");
> +
> + ctx->regs = devm_kcalloc(dev, ctx->pdata->num_vregs,
> + sizeof(*ctx->regs), GFP_KERNEL);
> + if (!ctx->regs)
> + return -ENOMEM;
> +
> + for (i = 0; i < ctx->pdata->num_vregs; i++)
> + ctx->regs[i].supply = ctx->pdata->vregs[i];
> +
> + ret = devm_regulator_bulk_get(dev, ctx->pdata->num_vregs, ctx->regs);
> + if (ret < 0)
> + return dev_err_probe(dev, PTR_ERR(ctx->regs),
> + "Failed to get all regulators\n");

Hi Bartosz,

It looks like ctx->regs is not an error pointer here,
should this be:

return dev_err_probe(dev, ret, ...

Flagged by Smatch.

> +
> + 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),
> + "Failed to get the Bluetooth enable GPIO\n");
> +
> + ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(ctx->wlan_gpio))
> + return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio),
> + "Failed to get the WLAN enable GPIO\n");
> +
> + ctx->clk = devm_clk_get_optional(dev, NULL);
> + if (IS_ERR(ctx->clk))
> + return dev_err_probe(dev, PTR_ERR(ctx->clk),
> + "Failed to get the reference clock\n");
> +
> + memset(&config, 0, sizeof(config));
> +
> + config.parent = dev;
> + config.owner = THIS_MODULE;
> + config.drvdata = ctx;
> + config.match = pwrseq_qcom_wcn_match;
> + config.targets = pwrseq_qcom_wcn_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;
> +}

...