linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/15] create power sequencing subsystem
@ 2021-08-17  0:54 Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 01/15] power: add power sequencer subsystem Dmitry Baryshkov
                   ` (15 more replies)
  0 siblings, 16 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

This is an RFC of the proposed power sequencer subsystem. This is a
generification of the MMC pwrseq code. The subsystem tries to abstract
the idea of complex power-up/power-down/reset of the devices.

The primary set of devices that promted me to create this patchset is
the Qualcomm BT+WiFi family of chips. They reside on serial+platform
interfaces (older generations) or on serial+PCIe (newer generations).
They require a set of external voltage regulators to be powered on and
(some of them) have separate WiFi and Bluetooth enable GPIOs.

This patchset being an RFC tries to demonstrate the approach, design and
usage of the pwrseq subsystem. Following issues are present in the RFC
at this moment but will be fixed later if the overall approach would be
viewed as acceptable:

 - No documentation
   While the code tries to be self-documenting proper documentation
   would be required.

 - Minimal device tree bindings changes
   There are no proper updates for the DT bindings (thus neither Rob
   Herring nor devicetree are included in the To/Cc lists). The dt
   schema changes would be a part of v1.

 - Lack of proper PCIe integration
   At this moment support for PCIe is hacked up to be able to test the
   PCIe part of qca6390. Proper PCIe support would require automatically
   powering up the devices before the scan basing on the proper device
   structure in the device tree.

----------------------------------------------------------------
Dmitry Baryshkov (15):
      power: add power sequencer subsystem
      pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
      mmc: core: switch to new pwrseq subsystem
      ath10k: add support for pwrseq sequencing
      Bluetooth: hci_qca: merge qca_power into qca_serdev
      Bluetooth: hci_qca: merge init paths
      Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
      Bluetooth: hci_qca: futher rework of power on/off handling
      Bluetooth: hci_qca: add support for pwrseq
      pwrseq: add support for QCA BT+WiFi power sequencer
      arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca power sequencer
      arm64: dts: qcom: qrb5165-rb5: add bluetooth support
      arm64: dts: qcom: sdm845-db845c: add second channel support to qca power sequencer
      WIP: PCI: qcom: use pwrseq to power up bus devices
      WIP: arm64: dts: qcom: qrb5165-rb5: add bus-pwrseq property to pcie0

 .../{mmc => power/pwrseq}/mmc-pwrseq-emmc.yaml     |   0
 .../{mmc => power/pwrseq}/mmc-pwrseq-sd8787.yaml   |   0
 .../{mmc => power/pwrseq}/mmc-pwrseq-simple.yaml   |   0
 arch/arm64/boot/dts/qcom/qrb5165-rb5.dts           |  51 +++
 arch/arm64/boot/dts/qcom/sdm845-db845c.dts         |  28 +-
 arch/arm64/boot/dts/qcom/sdm845.dtsi               |   6 +
 drivers/bluetooth/hci_qca.c                        | 286 +++++++-------
 drivers/mmc/core/Kconfig                           |  32 --
 drivers/mmc/core/Makefile                          |   4 -
 drivers/mmc/core/core.c                            |   9 +-
 drivers/mmc/core/host.c                            |   8 +-
 drivers/mmc/core/mmc.c                             |   3 +-
 drivers/mmc/core/pwrseq.c                          | 117 ------
 drivers/mmc/core/pwrseq.h                          |  58 ---
 drivers/mmc/core/pwrseq_emmc.c                     | 120 ------
 drivers/mmc/core/pwrseq_sd8787.c                   | 107 ------
 drivers/mmc/core/pwrseq_simple.c                   | 164 --------
 drivers/net/wireless/ath/ath10k/snoc.c             |  63 +++-
 drivers/net/wireless/ath/ath10k/snoc.h             |   2 +
 drivers/pci/controller/dwc/pcie-qcom.c             |  13 +
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/pwrseq/Kconfig                       |  57 +++
 drivers/power/pwrseq/Makefile                      |  11 +
 drivers/power/pwrseq/core.c                        | 411 +++++++++++++++++++++
 drivers/power/pwrseq/pwrseq_emmc.c                 | 118 ++++++
 drivers/power/pwrseq/pwrseq_qca.c                  | 291 +++++++++++++++
 drivers/power/pwrseq/pwrseq_sd8787.c               |  97 +++++
 drivers/power/pwrseq/pwrseq_simple.c               | 160 ++++++++
 include/linux/mmc/host.h                           |   4 +-
 include/linux/pwrseq/consumer.h                    |  88 +++++
 include/linux/pwrseq/driver.h                      |  71 ++++
 32 files changed, 1592 insertions(+), 789 deletions(-)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-emmc.yaml (100%)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-sd8787.yaml (100%)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-simple.yaml (100%)
 delete mode 100644 drivers/mmc/core/pwrseq.c
 delete mode 100644 drivers/mmc/core/pwrseq.h
 delete mode 100644 drivers/mmc/core/pwrseq_emmc.c
 delete mode 100644 drivers/mmc/core/pwrseq_sd8787.c
 delete mode 100644 drivers/mmc/core/pwrseq_simple.c
 create mode 100644 drivers/power/pwrseq/Kconfig
 create mode 100644 drivers/power/pwrseq/Makefile
 create mode 100644 drivers/power/pwrseq/core.c
 create mode 100644 drivers/power/pwrseq/pwrseq_emmc.c
 create mode 100644 drivers/power/pwrseq/pwrseq_qca.c
 create mode 100644 drivers/power/pwrseq/pwrseq_sd8787.c
 create mode 100644 drivers/power/pwrseq/pwrseq_simple.c
 create mode 100644 include/linux/pwrseq/consumer.h
 create mode 100644 include/linux/pwrseq/driver.h



^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 01/15] power: add power sequencer subsystem
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-19 23:37   ` Bjorn Andersson
  2021-08-17  0:54 ` [RFC PATCH 02/15] pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem Dmitry Baryshkov
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Basing on MMC's pwrseq support code, add separate power sequencer
subsystem. It will be used by other drivers to handle device power up
requirements.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/power/Kconfig           |   1 +
 drivers/power/Makefile          |   1 +
 drivers/power/pwrseq/Kconfig    |  11 +
 drivers/power/pwrseq/Makefile   |   6 +
 drivers/power/pwrseq/core.c     | 411 ++++++++++++++++++++++++++++++++
 include/linux/pwrseq/consumer.h |  88 +++++++
 include/linux/pwrseq/driver.h   |  71 ++++++
 7 files changed, 589 insertions(+)
 create mode 100644 drivers/power/pwrseq/Kconfig
 create mode 100644 drivers/power/pwrseq/Makefile
 create mode 100644 drivers/power/pwrseq/core.c
 create mode 100644 include/linux/pwrseq/consumer.h
 create mode 100644 include/linux/pwrseq/driver.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 696bf77a7042..c87cd2240a74 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
+source "drivers/power/pwrseq/Kconfig"
 source "drivers/power/reset/Kconfig"
 source "drivers/power/supply/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index effbf0377f32..1dbce454a8c4 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_POWER_SUPPLY)	+= supply/
+obj-$(CONFIG_PWRSEQ)		+= pwrseq/
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
new file mode 100644
index 000000000000..8904ec9ed541
--- /dev/null
+++ b/drivers/power/pwrseq/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig PWRSEQ
+	bool "Power Sequencer drivers"
+	help
+	  Provides support for special power sequencing drivers.
+
+	  Say Y here to enable support for such devices
+
+if PWRSEQ
+
+endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
new file mode 100644
index 000000000000..108429ff6445
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for power sequencer drivers.
+#
+
+obj-$(CONFIG_PWRSEQ) += core.o
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
new file mode 100644
index 000000000000..20485cae29aa
--- /dev/null
+++ b/drivers/power/pwrseq/core.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright 2021 (c) Linaro Ltd.
+// Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+//
+// Based on phy-core.c:
+// Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwrseq/consumer.h>
+#include <linux/pwrseq/driver.h>
+#include <linux/slab.h>
+
+#define	to_pwrseq(a)	(container_of((a), struct pwrseq, dev))
+
+static DEFINE_IDA(pwrseq_ida);
+static DEFINE_MUTEX(pwrseq_provider_mutex);
+static LIST_HEAD(pwrseq_provider_list);
+
+struct pwrseq_provider {
+	struct device		*dev;
+	struct module		*owner;
+	struct list_head	list;
+	void			*data;
+	struct pwrseq * (*of_xlate)(void *data, struct of_phandle_args *args);
+};
+
+void pwrseq_put(struct device *dev, struct pwrseq *pwrseq)
+{
+	device_link_remove(dev, &pwrseq->dev);
+
+	module_put(pwrseq->owner);
+	put_device(&pwrseq->dev);
+}
+EXPORT_SYMBOL_GPL(pwrseq_put);
+
+static struct pwrseq_provider *of_pwrseq_provider_lookup(struct device_node *node)
+{
+	struct pwrseq_provider *pwrseq_provider;
+
+	list_for_each_entry(pwrseq_provider, &pwrseq_provider_list, list) {
+		if (pwrseq_provider->dev->of_node == node)
+			return pwrseq_provider;
+	}
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+
+static struct pwrseq *_of_pwrseq_get(struct device *dev, const char *id)
+{
+	struct pwrseq_provider *pwrseq_provider;
+	struct pwrseq *pwrseq;
+	struct of_phandle_args args;
+	char prop_name[64]; /* 64 is max size of property name */
+	int ret;
+
+	snprintf(prop_name, 64, "%s-pwrseq", id);
+	ret = of_parse_phandle_with_args(dev->of_node, prop_name, "#pwrseq-cells", 0, &args);
+	if (ret) {
+		struct device_node *dn;
+
+		/*
+		 * Parsing failed. Try locating old bindings for mmc-pwrseq,
+		 * which did not use #pwrseq-cells.
+		 */
+		if (strcmp(id, "mmc"))
+			return ERR_PTR(-ENODEV);
+
+		dn = of_parse_phandle(dev->of_node, prop_name, 0);
+		if (!dn)
+			return ERR_PTR(-ENODEV);
+
+		args.np = dn;
+		args.args_count = 0;
+	}
+
+	mutex_lock(&pwrseq_provider_mutex);
+	pwrseq_provider = of_pwrseq_provider_lookup(args.np);
+	if (IS_ERR(pwrseq_provider) || !try_module_get(pwrseq_provider->owner)) {
+		pwrseq = ERR_PTR(-EPROBE_DEFER);
+		goto out_unlock;
+	}
+
+	if (!of_device_is_available(args.np)) {
+		dev_warn(pwrseq_provider->dev, "Requested pwrseq is disabled\n");
+		pwrseq = ERR_PTR(-ENODEV);
+		goto out_put_module;
+	}
+
+	pwrseq = pwrseq_provider->of_xlate(pwrseq_provider->data, &args);
+
+out_put_module:
+	module_put(pwrseq_provider->owner);
+
+out_unlock:
+	mutex_unlock(&pwrseq_provider_mutex);
+	of_node_put(args.np);
+
+	return pwrseq;
+}
+
+struct pwrseq *__must_check pwrseq_get(struct device *dev, const char *id)
+{
+	struct pwrseq *pwrseq;
+	struct device_link *link;
+
+	pwrseq = _of_pwrseq_get(dev, id);
+	if (IS_ERR(pwrseq))
+		return pwrseq;
+
+	if (!try_module_get(pwrseq->owner))
+		return ERR_PTR(-EPROBE_DEFER);
+
+	get_device(&pwrseq->dev);
+	link = device_link_add(dev, &pwrseq->dev, DL_FLAG_STATELESS);
+	if (!link)
+		dev_dbg(dev, "failed to create device link to %s\n",
+			dev_name(pwrseq->dev.parent));
+
+	return pwrseq;
+}
+EXPORT_SYMBOL_GPL(pwrseq_get);
+
+static void devm_pwrseq_release(struct device *dev, void *res)
+{
+	struct pwrseq *pwrseq = *(struct pwrseq **)res;
+
+	pwrseq_put(dev, pwrseq);
+}
+
+struct pwrseq *__must_check devm_pwrseq_get(struct device *dev, const char *id)
+{
+	struct pwrseq **ptr, *pwrseq;
+
+	ptr = devres_alloc(devm_pwrseq_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	pwrseq = pwrseq_get(dev, id);
+	if (!IS_ERR(pwrseq)) {
+		*ptr = pwrseq;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return pwrseq;
+}
+EXPORT_SYMBOL_GPL(devm_pwrseq_get);
+
+struct pwrseq *__must_check pwrseq_get_optional(struct device *dev, const char *id)
+{
+	struct pwrseq *pwrseq = pwrseq_get(dev, id);
+
+	if (pwrseq == ERR_PTR(-ENODEV))
+		return NULL;
+
+	return pwrseq;
+}
+EXPORT_SYMBOL_GPL(pwrseq_get_optional);
+
+struct pwrseq *__must_check devm_pwrseq_get_optional(struct device *dev, const char *id)
+{
+	struct pwrseq **ptr, *pwrseq;
+
+	ptr = devres_alloc(devm_pwrseq_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	pwrseq = pwrseq_get_optional(dev, id);
+	if (!IS_ERR_OR_NULL(pwrseq)) {
+		*ptr = pwrseq;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return pwrseq;
+}
+EXPORT_SYMBOL_GPL(devm_pwrseq_get_optional);
+
+int pwrseq_pre_power_on(struct pwrseq *pwrseq)
+{
+	if (pwrseq && pwrseq->ops->pre_power_on)
+		return pwrseq->ops->pre_power_on(pwrseq);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_pre_power_on);
+
+int pwrseq_power_on(struct pwrseq *pwrseq)
+{
+	if (pwrseq && pwrseq->ops->power_on)
+		return pwrseq->ops->power_on(pwrseq);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pwrseq_power_on);
+
+void pwrseq_power_off(struct pwrseq *pwrseq)
+{
+	if (pwrseq && pwrseq->ops->power_off)
+		pwrseq->ops->power_off(pwrseq);
+}
+EXPORT_SYMBOL_GPL(pwrseq_power_off);
+
+void pwrseq_reset(struct pwrseq *pwrseq)
+{
+	if (pwrseq && pwrseq->ops->reset)
+		pwrseq->ops->reset(pwrseq);
+}
+EXPORT_SYMBOL_GPL(pwrseq_reset);
+
+static void pwrseq_dev_release(struct device *dev)
+{
+	struct pwrseq *pwrseq = to_pwrseq(dev);
+
+	ida_free(&pwrseq_ida, pwrseq->id);
+	of_node_put(dev->of_node);
+	kfree(pwrseq);
+}
+
+static struct class pwrseq_class = {
+	.name = "pwrseq",
+	.dev_release = pwrseq_dev_release,
+};
+
+struct pwrseq *__pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops, void *data)
+{
+	struct pwrseq *pwrseq;
+	int ret;
+
+	if (WARN_ON(!dev))
+		return ERR_PTR(-EINVAL);
+
+	pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
+	if (!pwrseq)
+		return ERR_PTR(-ENOMEM);
+
+	ret = ida_alloc(&pwrseq_ida, GFP_KERNEL);
+	if (ret < 0)
+		goto free_pwrseq;
+
+	pwrseq->id = ret;
+
+	device_initialize(&pwrseq->dev);
+
+	pwrseq->dev.class = &pwrseq_class;
+	pwrseq->dev.parent = dev;
+	pwrseq->dev.of_node = of_node_get(dev->of_node);
+	pwrseq->ops = ops;
+	pwrseq->owner = owner;
+
+	dev_set_drvdata(&pwrseq->dev, data);
+
+	ret = dev_set_name(&pwrseq->dev, "pwrseq-%s.%u", dev_name(dev), pwrseq->id);
+	if (ret)
+		goto put_dev;
+
+	ret = device_add(&pwrseq->dev);
+	if (ret)
+		goto put_dev;
+
+	if (pm_runtime_enabled(dev)) {
+		pm_runtime_enable(&pwrseq->dev);
+		pm_runtime_no_callbacks(&pwrseq->dev);
+	}
+
+	return pwrseq;
+
+put_dev:
+	/* will call pwrseq_dev_release() to free resources */
+	put_device(&pwrseq->dev);
+
+	return ERR_PTR(ret);
+
+free_pwrseq:
+	kfree(pwrseq);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(__pwrseq_create);
+
+void pwrseq_destroy(struct pwrseq *pwrseq)
+{
+	pm_runtime_disable(&pwrseq->dev);
+	device_unregister(&pwrseq->dev);
+}
+EXPORT_SYMBOL_GPL(pwrseq_destroy);
+
+static void devm_pwrseq_destroy(struct device *dev, void *res)
+{
+	struct pwrseq *pwrseq = *(struct pwrseq **)res;
+
+	pwrseq_destroy(pwrseq);
+}
+
+struct pwrseq *__devm_pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops, void *data)
+{
+	struct pwrseq **ptr, *pwrseq;
+
+	ptr = devres_alloc(devm_pwrseq_destroy, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	pwrseq = __pwrseq_create(dev, owner, ops, data);
+	if (!IS_ERR(pwrseq)) {
+		*ptr = pwrseq;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return pwrseq;
+}
+EXPORT_SYMBOL_GPL(__devm_pwrseq_create);
+
+struct pwrseq_provider *__of_pwrseq_provider_register(struct device *dev,
+	struct module *owner,
+	struct pwrseq * (*of_xlate)(void *data,
+				    struct of_phandle_args *args),
+	void *data)
+{
+	struct pwrseq_provider *pwrseq_provider;
+
+	pwrseq_provider = kzalloc(sizeof(*pwrseq_provider), GFP_KERNEL);
+	if (!pwrseq_provider)
+		return ERR_PTR(-ENOMEM);
+
+	pwrseq_provider->dev = dev;
+	pwrseq_provider->owner = owner;
+	pwrseq_provider->of_xlate = of_xlate;
+	pwrseq_provider->data = data;
+
+	mutex_lock(&pwrseq_provider_mutex);
+	list_add_tail(&pwrseq_provider->list, &pwrseq_provider_list);
+	mutex_unlock(&pwrseq_provider_mutex);
+
+	return pwrseq_provider;
+}
+EXPORT_SYMBOL_GPL(__of_pwrseq_provider_register);
+
+void of_pwrseq_provider_unregister(struct pwrseq_provider *pwrseq_provider)
+{
+	if (IS_ERR(pwrseq_provider))
+		return;
+
+	mutex_lock(&pwrseq_provider_mutex);
+	list_del(&pwrseq_provider->list);
+	kfree(pwrseq_provider);
+	mutex_unlock(&pwrseq_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_provider_unregister);
+
+static void devm_pwrseq_provider_unregister(struct device *dev, void *res)
+{
+	struct pwrseq_provider *pwrseq_provider = *(struct pwrseq_provider **)res;
+
+	of_pwrseq_provider_unregister(pwrseq_provider);
+}
+
+struct pwrseq_provider *__devm_of_pwrseq_provider_register(struct device *dev,
+	struct module *owner,
+	struct pwrseq * (*of_xlate)(void *data,
+				    struct of_phandle_args *args),
+	void *data)
+{
+	struct pwrseq_provider **ptr, *pwrseq_provider;
+
+	ptr = devres_alloc(devm_pwrseq_provider_unregister, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	pwrseq_provider = __of_pwrseq_provider_register(dev, owner, of_xlate, data);
+	if (!IS_ERR(pwrseq_provider)) {
+		*ptr = pwrseq_provider;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return pwrseq_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_pwrseq_provider_register);
+
+struct pwrseq *of_pwrseq_xlate_onecell(void *data, struct of_phandle_args *args)
+{
+	struct pwrseq_onecell_data *pwrseq_data = data;
+	unsigned int idx;
+
+	if (args->args_count != 1)
+		return ERR_PTR(-EINVAL);
+
+	idx = args->args[0];
+	if (idx >= pwrseq_data->num) {
+		pr_err("%s: invalid index %u\n", __func__, idx);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return pwrseq_data->pwrseqs[idx];
+}
+
+static int __init pwrseq_core_init(void)
+{
+	return class_register(&pwrseq_class);
+}
+device_initcall(pwrseq_core_init);
diff --git a/include/linux/pwrseq/consumer.h b/include/linux/pwrseq/consumer.h
new file mode 100644
index 000000000000..fbcdc1fc0751
--- /dev/null
+++ b/include/linux/pwrseq/consumer.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ */
+
+#ifndef __LINUX_PWRSEQ_CONSUMER_H__
+#define __LINUX_PWRSEQ_CONSUMER_H__
+
+struct pwrseq;
+struct device;
+
+#if defined(CONFIG_PWRSEQ)
+
+struct pwrseq *__must_check pwrseq_get(struct device *dev, const char *id);
+struct pwrseq *__must_check devm_pwrseq_get(struct device *dev, const char *id);
+
+struct pwrseq *__must_check pwrseq_get_optional(struct device *dev, const char *id);
+struct pwrseq *__must_check devm_pwrseq_get_optional(struct device *dev, const char *id);
+
+void pwrseq_put(struct device *dev, struct pwrseq *pwrseq);
+
+int pwrseq_pre_power_on(struct pwrseq *pwrseq);
+int pwrseq_power_on(struct pwrseq *pwrseq);
+void pwrseq_power_off(struct pwrseq *pwrseq);
+void pwrseq_reset(struct pwrseq *pwrseq);
+
+#else
+
+static inline struct pwrseq *__must_check
+pwrseq_get(struct device *dev, const char *id)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
+static inline struct pwrseq *__must_check
+devm_pwrseq_get(struct device *dev, const char *id)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
+static inline struct pwrseq *__must_check
+pwrseq_get_optional(struct device *dev, const char *id)
+{
+	return NULL;
+}
+
+static inline struct pwrseq *__must_check
+devm_pwrseq_get_optional(struct device *dev, const char *id)
+{
+	return NULL;
+}
+
+static inline void pwrseq_put(struct device *dev, struct pwrseq *pwrseq)
+{
+}
+
+static inline int pwrseq_pre_power_on(struct pwrseq *pwrseq)
+{
+	return -ENOSYS;
+}
+
+static inline int pwrseq_power_on(struct pwrseq *pwrseq)
+{
+	return -ENOSYS;
+}
+
+static inline void pwrseq_power_off(struct pwrseq *pwrseq)
+{
+}
+
+static inline void pwrseq_reset(struct pwrseq *pwrseq)
+{
+}
+
+#endif
+
+static inline int pwrseq_full_power_on(struct pwrseq *pwrseq)
+{
+	int ret;
+
+	ret = pwrseq_pre_power_on(pwrseq);
+	if (ret)
+		return ret;
+
+	return pwrseq_power_on(pwrseq);
+}
+
+#endif /* __LINUX_PWRSEQ_CONSUMER_H__ */
diff --git a/include/linux/pwrseq/driver.h b/include/linux/pwrseq/driver.h
new file mode 100644
index 000000000000..6d7c66525134
--- /dev/null
+++ b/include/linux/pwrseq/driver.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2021 Linaro Ltd.
+ */
+
+#ifndef __LINUX_PWRSEQ_DRIVER_H__
+#define __LINUX_PWRSEQ_DRIVER_H__
+
+#include <linux/of.h>
+
+struct pwrseq;
+
+struct pwrseq_ops {
+	int (*pre_power_on)(struct pwrseq *pwrseq);
+	int (*power_on)(struct pwrseq *pwrseq);
+	void (*power_off)(struct pwrseq *pwrseq);
+	void (*reset)(struct pwrseq *pwrseq);
+};
+
+struct pwrseq {
+	struct device dev;
+	const struct pwrseq_ops *ops;
+	unsigned int id;
+	struct module *owner;
+};
+
+struct pwrseq *__pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops, void *data);
+struct pwrseq *__devm_pwrseq_create(struct device *dev, struct module *owner, const struct pwrseq_ops *ops, void *data);
+
+#define pwrseq_create(dev, ops, data) __pwrseq_create((dev), THIS_MODULE, (ops), (data))
+#define devm_pwrseq_create(dev, ops, data) __devm_pwrseq_create((dev), THIS_MODULE, (ops), (data))
+
+void pwrseq_destroy(struct pwrseq *pwrseq);
+
+static inline void *pwrseq_get_data(struct pwrseq *pwrseq)
+{
+	return dev_get_drvdata(&pwrseq->dev);
+}
+
+#define	of_pwrseq_provider_register(dev, xlate, data)	\
+	__of_pwrseq_provider_register((dev), THIS_MODULE, (xlate), (data))
+
+#define	devm_of_pwrseq_provider_register(dev, xlate, data)	\
+	__devm_of_pwrseq_provider_register((dev), THIS_MODULE, (xlate), (data))
+
+struct pwrseq_provider *__of_pwrseq_provider_register(struct device *dev,
+	struct module *owner,
+	struct pwrseq * (*of_xlate)(void *data,
+				    struct of_phandle_args *args),
+	void *data);
+struct pwrseq_provider *__devm_of_pwrseq_provider_register(struct device *dev,
+	struct module *owner,
+	struct pwrseq * (*of_xlate)(void *data,
+				    struct of_phandle_args *args),
+	void *data);
+void of_pwrseq_provider_unregister(struct pwrseq_provider *pwrseq_provider);
+
+static inline struct pwrseq *of_pwrseq_xlate_single(void *data,
+			       struct of_phandle_args *args)
+{
+	return data;
+}
+
+struct pwrseq_onecell_data {
+	unsigned int num;
+	struct pwrseq *pwrseqs[];
+};
+
+struct pwrseq *of_pwrseq_xlate_onecell(void *data, struct of_phandle_args *args);
+
+#endif /* __LINUX_PWRSEQ_DRIVER_H__ */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 02/15] pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 01/15] power: add power sequencer subsystem Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 03/15] mmc: core: switch " Dmitry Baryshkov
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Port MMC's all pwrseq drivers to new pwrseq subsystem.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 .../pwrseq}/mmc-pwrseq-emmc.yaml              |   0
 .../pwrseq}/mmc-pwrseq-sd8787.yaml            |   0
 .../pwrseq}/mmc-pwrseq-simple.yaml            |   0
 drivers/mmc/core/Kconfig                      |  32 ----
 drivers/mmc/core/Makefile                     |   3 -
 drivers/mmc/core/pwrseq_emmc.c                | 120 -------------
 drivers/mmc/core/pwrseq_sd8787.c              | 107 ------------
 drivers/mmc/core/pwrseq_simple.c              | 164 ------------------
 drivers/power/pwrseq/Kconfig                  |  32 ++++
 drivers/power/pwrseq/Makefile                 |   4 +
 drivers/power/pwrseq/pwrseq_emmc.c            | 118 +++++++++++++
 drivers/power/pwrseq/pwrseq_sd8787.c          |  97 +++++++++++
 drivers/power/pwrseq/pwrseq_simple.c          | 160 +++++++++++++++++
 13 files changed, 411 insertions(+), 426 deletions(-)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-emmc.yaml (100%)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-sd8787.yaml (100%)
 rename Documentation/devicetree/bindings/{mmc => power/pwrseq}/mmc-pwrseq-simple.yaml (100%)
 delete mode 100644 drivers/mmc/core/pwrseq_emmc.c
 delete mode 100644 drivers/mmc/core/pwrseq_sd8787.c
 delete mode 100644 drivers/mmc/core/pwrseq_simple.c
 create mode 100644 drivers/power/pwrseq/pwrseq_emmc.c
 create mode 100644 drivers/power/pwrseq/pwrseq_sd8787.c
 create mode 100644 drivers/power/pwrseq/pwrseq_simple.c

diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-emmc.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-emmc.yaml
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-sd8787.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-sd8787.yaml
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml b/Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.yaml
similarity index 100%
rename from Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml
rename to Documentation/devicetree/bindings/power/pwrseq/mmc-pwrseq-simple.yaml
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ae8b69aee619..cf7df64ce009 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -2,38 +2,6 @@
 #
 # MMC core configuration
 #
-config PWRSEQ_EMMC
-	tristate "HW reset support for eMMC"
-	default y
-	depends on OF
-	help
-	  This selects Hardware reset support aka pwrseq-emmc for eMMC
-	  devices. By default this option is set to y.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called pwrseq_emmc.
-
-config PWRSEQ_SD8787
-	tristate "HW reset support for SD8787 BT + Wifi module"
-	depends on OF && (MWIFIEX || BT_MRVL_SDIO || LIBERTAS_SDIO)
-	help
-	  This selects hardware reset support for the SD8787 BT + Wifi
-	  module. By default this option is set to n.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called pwrseq_sd8787.
-
-config PWRSEQ_SIMPLE
-	tristate "Simple HW reset support for MMC"
-	default y
-	depends on OF
-	help
-	  This selects simple hardware reset support aka pwrseq-simple for MMC
-	  devices. By default this option is set to y.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called pwrseq_simple.
-
 config MMC_BLOCK
 	tristate "MMC block device driver"
 	depends on BLOCK
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 6a907736cd7a..322eb69bd00e 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -10,9 +10,6 @@ mmc_core-y			:= core.o bus.o host.o \
 				   sdio_cis.o sdio_io.o sdio_irq.o \
 				   slot-gpio.o regulator.o
 mmc_core-$(CONFIG_OF)		+= pwrseq.o
-obj-$(CONFIG_PWRSEQ_SIMPLE)	+= pwrseq_simple.o
-obj-$(CONFIG_PWRSEQ_SD8787)	+= pwrseq_sd8787.o
-obj-$(CONFIG_PWRSEQ_EMMC)	+= pwrseq_emmc.o
 mmc_core-$(CONFIG_DEBUG_FS)	+= debugfs.o
 obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 mmc_block-objs			:= block.o queue.o
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
deleted file mode 100644
index f6dde9edd7a3..000000000000
--- a/drivers/mmc/core/pwrseq_emmc.c
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2015, Samsung Electronics Co., Ltd.
- *
- * Author: Marek Szyprowski <m.szyprowski@samsung.com>
- *
- * Simple eMMC hardware reset provider
- */
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/gpio/consumer.h>
-#include <linux/reboot.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-struct mmc_pwrseq_emmc {
-	struct mmc_pwrseq pwrseq;
-	struct notifier_block reset_nb;
-	struct gpio_desc *reset_gpio;
-};
-
-#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq)
-
-static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
-{
-	struct mmc_pwrseq_emmc *pwrseq =  to_pwrseq_emmc(host->pwrseq);
-
-	gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
-	udelay(1);
-	gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
-	udelay(200);
-}
-
-static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
-				    unsigned long mode, void *cmd)
-{
-	struct mmc_pwrseq_emmc *pwrseq = container_of(this,
-					struct mmc_pwrseq_emmc, reset_nb);
-	gpiod_set_value(pwrseq->reset_gpio, 1);
-	udelay(1);
-	gpiod_set_value(pwrseq->reset_gpio, 0);
-	udelay(200);
-
-	return NOTIFY_DONE;
-}
-
-static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
-	.reset = mmc_pwrseq_emmc_reset,
-};
-
-static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_emmc *pwrseq;
-	struct device *dev = &pdev->dev;
-
-	pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
-	if (!pwrseq)
-		return -ENOMEM;
-
-	pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(pwrseq->reset_gpio))
-		return PTR_ERR(pwrseq->reset_gpio);
-
-	if (!gpiod_cansleep(pwrseq->reset_gpio)) {
-		/*
-		 * register reset handler to ensure emmc reset also from
-		 * emergency_reboot(), priority 255 is the highest priority
-		 * so it will be executed before any system reboot handler.
-		 */
-		pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
-		pwrseq->reset_nb.priority = 255;
-		register_restart_handler(&pwrseq->reset_nb);
-	} else {
-		dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
-	}
-
-	pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
-	pwrseq->pwrseq.dev = dev;
-	pwrseq->pwrseq.owner = THIS_MODULE;
-	platform_set_drvdata(pdev, pwrseq);
-
-	return mmc_pwrseq_register(&pwrseq->pwrseq);
-}
-
-static int mmc_pwrseq_emmc_remove(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev);
-
-	unregister_restart_handler(&pwrseq->reset_nb);
-	mmc_pwrseq_unregister(&pwrseq->pwrseq);
-
-	return 0;
-}
-
-static const struct of_device_id mmc_pwrseq_emmc_of_match[] = {
-	{ .compatible = "mmc-pwrseq-emmc",},
-	{/* sentinel */},
-};
-
-MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match);
-
-static struct platform_driver mmc_pwrseq_emmc_driver = {
-	.probe = mmc_pwrseq_emmc_probe,
-	.remove = mmc_pwrseq_emmc_remove,
-	.driver = {
-		.name = "pwrseq_emmc",
-		.of_match_table = mmc_pwrseq_emmc_of_match,
-	},
-};
-
-module_platform_driver(mmc_pwrseq_emmc_driver);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/pwrseq_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c
deleted file mode 100644
index 68a826f1c0a1..000000000000
--- a/drivers/mmc/core/pwrseq_sd8787.c
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
- *
- * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
- *
- * Based on the original work pwrseq_simple.c
- *  Copyright (C) 2014 Linaro Ltd
- *  Author: Ulf Hansson <ulf.hansson@linaro.org>
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/gpio/consumer.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-struct mmc_pwrseq_sd8787 {
-	struct mmc_pwrseq pwrseq;
-	struct gpio_desc *reset_gpio;
-	struct gpio_desc *pwrdn_gpio;
-};
-
-#define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq)
-
-static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host)
-{
-	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
-
-	gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
-
-	msleep(300);
-	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
-}
-
-static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host)
-{
-	struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
-
-	gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
-	gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
-}
-
-static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = {
-	.pre_power_on = mmc_pwrseq_sd8787_pre_power_on,
-	.power_off = mmc_pwrseq_sd8787_power_off,
-};
-
-static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = {
-	{ .compatible = "mmc-pwrseq-sd8787",},
-	{/* sentinel */},
-};
-MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match);
-
-static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_sd8787 *pwrseq;
-	struct device *dev = &pdev->dev;
-
-	pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
-	if (!pwrseq)
-		return -ENOMEM;
-
-	pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
-	if (IS_ERR(pwrseq->pwrdn_gpio))
-		return PTR_ERR(pwrseq->pwrdn_gpio);
-
-	pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(pwrseq->reset_gpio))
-		return PTR_ERR(pwrseq->reset_gpio);
-
-	pwrseq->pwrseq.dev = dev;
-	pwrseq->pwrseq.ops = &mmc_pwrseq_sd8787_ops;
-	pwrseq->pwrseq.owner = THIS_MODULE;
-	platform_set_drvdata(pdev, pwrseq);
-
-	return mmc_pwrseq_register(&pwrseq->pwrseq);
-}
-
-static int mmc_pwrseq_sd8787_remove(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_sd8787 *pwrseq = platform_get_drvdata(pdev);
-
-	mmc_pwrseq_unregister(&pwrseq->pwrseq);
-
-	return 0;
-}
-
-static struct platform_driver mmc_pwrseq_sd8787_driver = {
-	.probe = mmc_pwrseq_sd8787_probe,
-	.remove = mmc_pwrseq_sd8787_remove,
-	.driver = {
-		.name = "pwrseq_sd8787",
-		.of_match_table = mmc_pwrseq_sd8787_of_match,
-	},
-};
-
-module_platform_driver(mmc_pwrseq_sd8787_driver);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
deleted file mode 100644
index ea4d3670560e..000000000000
--- a/drivers/mmc/core/pwrseq_simple.c
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  Copyright (C) 2014 Linaro Ltd
- *
- * Author: Ulf Hansson <ulf.hansson@linaro.org>
- *
- *  Simple MMC power sequence management
- */
-#include <linux/clk.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/gpio/consumer.h>
-#include <linux/delay.h>
-#include <linux/property.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-struct mmc_pwrseq_simple {
-	struct mmc_pwrseq pwrseq;
-	bool clk_enabled;
-	u32 post_power_on_delay_ms;
-	u32 power_off_delay_us;
-	struct clk *ext_clk;
-	struct gpio_descs *reset_gpios;
-};
-
-#define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq)
-
-static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
-					      int value)
-{
-	struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
-
-	if (!IS_ERR(reset_gpios)) {
-		unsigned long *values;
-		int nvalues = reset_gpios->ndescs;
-
-		values = bitmap_alloc(nvalues, GFP_KERNEL);
-		if (!values)
-			return;
-
-		if (value)
-			bitmap_fill(values, nvalues);
-		else
-			bitmap_zero(values, nvalues);
-
-		gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
-					       reset_gpios->info, values);
-
-		kfree(values);
-	}
-}
-
-static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
-{
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
-	if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
-		clk_prepare_enable(pwrseq->ext_clk);
-		pwrseq->clk_enabled = true;
-	}
-
-	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
-}
-
-static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
-{
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
-	mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
-
-	if (pwrseq->post_power_on_delay_ms)
-		msleep(pwrseq->post_power_on_delay_ms);
-}
-
-static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
-{
-	struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
-
-	mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
-
-	if (pwrseq->power_off_delay_us)
-		usleep_range(pwrseq->power_off_delay_us,
-			2 * pwrseq->power_off_delay_us);
-
-	if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) {
-		clk_disable_unprepare(pwrseq->ext_clk);
-		pwrseq->clk_enabled = false;
-	}
-}
-
-static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
-	.pre_power_on = mmc_pwrseq_simple_pre_power_on,
-	.post_power_on = mmc_pwrseq_simple_post_power_on,
-	.power_off = mmc_pwrseq_simple_power_off,
-};
-
-static const struct of_device_id mmc_pwrseq_simple_of_match[] = {
-	{ .compatible = "mmc-pwrseq-simple",},
-	{/* sentinel */},
-};
-MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match);
-
-static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_simple *pwrseq;
-	struct device *dev = &pdev->dev;
-
-	pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
-	if (!pwrseq)
-		return -ENOMEM;
-
-	pwrseq->ext_clk = devm_clk_get(dev, "ext_clock");
-	if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT)
-		return PTR_ERR(pwrseq->ext_clk);
-
-	pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset",
-							GPIOD_OUT_HIGH);
-	if (IS_ERR(pwrseq->reset_gpios) &&
-	    PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
-	    PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
-		return PTR_ERR(pwrseq->reset_gpios);
-	}
-
-	device_property_read_u32(dev, "post-power-on-delay-ms",
-				 &pwrseq->post_power_on_delay_ms);
-	device_property_read_u32(dev, "power-off-delay-us",
-				 &pwrseq->power_off_delay_us);
-
-	pwrseq->pwrseq.dev = dev;
-	pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
-	pwrseq->pwrseq.owner = THIS_MODULE;
-	platform_set_drvdata(pdev, pwrseq);
-
-	return mmc_pwrseq_register(&pwrseq->pwrseq);
-}
-
-static int mmc_pwrseq_simple_remove(struct platform_device *pdev)
-{
-	struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev);
-
-	mmc_pwrseq_unregister(&pwrseq->pwrseq);
-
-	return 0;
-}
-
-static struct platform_driver mmc_pwrseq_simple_driver = {
-	.probe = mmc_pwrseq_simple_probe,
-	.remove = mmc_pwrseq_simple_remove,
-	.driver = {
-		.name = "pwrseq_simple",
-		.of_match_table = mmc_pwrseq_simple_of_match,
-	},
-};
-
-module_platform_driver(mmc_pwrseq_simple_driver);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
index 8904ec9ed541..36339a456b03 100644
--- a/drivers/power/pwrseq/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -8,4 +8,36 @@ menuconfig PWRSEQ
 
 if PWRSEQ
 
+config PWRSEQ_EMMC
+	tristate "HW reset support for eMMC"
+	default y
+	depends on OF
+	help
+	  This selects Hardware reset support aka pwrseq-emmc for eMMC
+	  devices. By default this option is set to y.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pwrseq_emmc.
+
+config PWRSEQ_SD8787
+	tristate "HW reset support for SD8787 BT + Wifi module"
+	depends on OF
+	help
+	  This selects hardware reset support for the SD8787 BT + Wifi
+	  module. By default this option is set to n.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pwrseq_sd8787.
+
+config PWRSEQ_SIMPLE
+	tristate "Simple HW reset support"
+	default y
+	depends on OF
+	help
+	  This selects simple hardware reset support aka pwrseq-simple.
+	  By default this option is set to y.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pwrseq_simple.
+
 endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 108429ff6445..6f359d228843 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -4,3 +4,7 @@
 #
 
 obj-$(CONFIG_PWRSEQ) += core.o
+
+obj-$(CONFIG_PWRSEQ_EMMC)	+= pwrseq_emmc.o
+obj-$(CONFIG_PWRSEQ_SD8787)	+= pwrseq_sd8787.o
+obj-$(CONFIG_PWRSEQ_SIMPLE)	+= pwrseq_simple.o
diff --git a/drivers/power/pwrseq/pwrseq_emmc.c b/drivers/power/pwrseq/pwrseq_emmc.c
new file mode 100644
index 000000000000..a56969e18b1a
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_emmc.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015, Samsung Electronics Co., Ltd.
+ *
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * Simple eMMC hardware reset provider
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/reboot.h>
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_emmc {
+	struct notifier_block reset_nb;
+	struct gpio_desc *reset_gpio;
+};
+
+static void pwrseq_ereset(struct pwrseq *pwrseq)
+{
+	struct pwrseq_emmc *pwrseq_emmc = pwrseq_get_data(pwrseq);
+
+	gpiod_set_value_cansleep(pwrseq_emmc->reset_gpio, 1);
+	udelay(1);
+	gpiod_set_value_cansleep(pwrseq_emmc->reset_gpio, 0);
+	udelay(200);
+}
+
+static int pwrseq_ereset_nb(struct notifier_block *this,
+				    unsigned long mode, void *cmd)
+{
+	struct pwrseq_emmc *pwrseq_emmc = container_of(this,
+					struct pwrseq_emmc, reset_nb);
+	gpiod_set_value(pwrseq_emmc->reset_gpio, 1);
+	udelay(1);
+	gpiod_set_value(pwrseq_emmc->reset_gpio, 0);
+	udelay(200);
+
+	return NOTIFY_DONE;
+}
+
+static const struct pwrseq_ops pwrseq_eops = {
+	.reset = pwrseq_ereset,
+};
+
+static int pwrseq_eprobe(struct platform_device *pdev)
+{
+	struct pwrseq_emmc *pwrseq_emmc;
+	struct pwrseq *pwrseq;
+	struct pwrseq_provider *provider;
+	struct device *dev = &pdev->dev;
+
+	pwrseq_emmc = devm_kzalloc(dev, sizeof(*pwrseq_emmc), GFP_KERNEL);
+	if (!pwrseq_emmc)
+		return -ENOMEM;
+
+	pwrseq_emmc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(pwrseq_emmc->reset_gpio))
+		return PTR_ERR(pwrseq_emmc->reset_gpio);
+
+	if (!gpiod_cansleep(pwrseq_emmc->reset_gpio)) {
+		/*
+		 * register reset handler to ensure emmc reset also from
+		 * emergency_reboot(), priority 255 is the highest priority
+		 * so it will be executed before any system reboot handler.
+		 */
+		pwrseq_emmc->reset_nb.notifier_call = pwrseq_ereset_nb;
+		pwrseq_emmc->reset_nb.priority = 255;
+		register_restart_handler(&pwrseq_emmc->reset_nb);
+	} else {
+		dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
+	}
+
+	platform_set_drvdata(pdev, pwrseq_emmc);
+
+	pwrseq = devm_pwrseq_create(dev, &pwrseq_eops, pwrseq_emmc);
+	if (IS_ERR(pwrseq))
+		return PTR_ERR(pwrseq);
+
+	provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static int pwrseq_eremove(struct platform_device *pdev)
+{
+	struct pwrseq_emmc *pwrseq_emmc = platform_get_drvdata(pdev);
+
+	unregister_restart_handler(&pwrseq_emmc->reset_nb);
+
+	return 0;
+}
+
+static const struct of_device_id pwrseq_eof_match[] = {
+	{ .compatible = "mmc-pwrseq-emmc",},
+	{/* sentinel */},
+};
+
+MODULE_DEVICE_TABLE(of, pwrseq_eof_match);
+
+static struct platform_driver pwrseq_edriver = {
+	.probe = pwrseq_eprobe,
+	.remove = pwrseq_eremove,
+	.driver = {
+		.name = "pwrseq_emmc",
+		.of_match_table = pwrseq_eof_match,
+	},
+};
+
+module_platform_driver(pwrseq_edriver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pwrseq/pwrseq_sd8787.c b/drivers/power/pwrseq/pwrseq_sd8787.c
new file mode 100644
index 000000000000..7759097dd4d6
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_sd8787.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
+ *
+ * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
+ *
+ * Based on the original work pwrseq_sd8787.c
+ *  Copyright (C) 2014 Linaro Ltd
+ *  Author: Ulf Hansson <ulf.hansson@linaro.org>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_sd8787 {
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwrdn_gpio;
+};
+
+static int pwrseq_sd8787_pre_power_on(struct pwrseq *pwrseq)
+{
+	struct pwrseq_sd8787 *pwrseq_sd8787 = pwrseq_get_data(pwrseq);
+
+	gpiod_set_value_cansleep(pwrseq_sd8787->reset_gpio, 1);
+
+	msleep(300);
+	gpiod_set_value_cansleep(pwrseq_sd8787->pwrdn_gpio, 1);
+
+	return 0;
+}
+
+static void pwrseq_sd8787_power_off(struct pwrseq *pwrseq)
+{
+	struct pwrseq_sd8787 *pwrseq_sd8787 = pwrseq_get_data(pwrseq);
+
+	gpiod_set_value_cansleep(pwrseq_sd8787->pwrdn_gpio, 0);
+	gpiod_set_value_cansleep(pwrseq_sd8787->reset_gpio, 0);
+}
+
+static const struct pwrseq_ops pwrseq_sd8787_ops = {
+	.pre_power_on = pwrseq_sd8787_pre_power_on,
+	.power_off = pwrseq_sd8787_power_off,
+};
+
+static const struct of_device_id pwrseq_sd8787_of_match[] = {
+	{ .compatible = "mmc-pwrseq-sd8787",},
+	{/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, pwrseq_sd8787_of_match);
+
+static int pwrseq_sd8787_probe(struct platform_device *pdev)
+{
+	struct pwrseq_sd8787 *pwrseq_sd8787;
+	struct pwrseq *pwrseq;
+	struct pwrseq_provider *provider;
+	struct device *dev = &pdev->dev;
+
+	pwrseq_sd8787 = devm_kzalloc(dev, sizeof(*pwrseq_sd8787), GFP_KERNEL);
+	if (!pwrseq_sd8787)
+		return -ENOMEM;
+
+	pwrseq_sd8787->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
+	if (IS_ERR(pwrseq_sd8787->pwrdn_gpio))
+		return PTR_ERR(pwrseq_sd8787->pwrdn_gpio);
+
+	pwrseq_sd8787->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(pwrseq_sd8787->reset_gpio))
+		return PTR_ERR(pwrseq_sd8787->reset_gpio);
+
+	pwrseq = devm_pwrseq_create(dev, &pwrseq_sd8787_ops, pwrseq_sd8787);
+	if (IS_ERR(pwrseq))
+		return PTR_ERR(pwrseq);
+
+	provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver pwrseq_sd8787_driver = {
+	.probe = pwrseq_sd8787_probe,
+	.driver = {
+		.name = "pwrseq_sd8787",
+		.of_match_table = pwrseq_sd8787_of_match,
+	},
+};
+
+module_platform_driver(pwrseq_sd8787_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pwrseq/pwrseq_simple.c b/drivers/power/pwrseq/pwrseq_simple.c
new file mode 100644
index 000000000000..f8d6e6e077df
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_simple.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ *  Simple MMC power sequence management
+ */
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/pwrseq/driver.h>
+
+struct pwrseq_simple {
+	bool clk_enabled;
+	u32 post_power_on_delay_ms;
+	u32 power_off_delay_us;
+	struct clk *ext_clk;
+	struct gpio_descs *reset_gpios;
+};
+
+static int pwrseq_simple_set_gpios_value(struct pwrseq_simple *pwrseq_simple,
+					 int value)
+{
+	struct gpio_descs *reset_gpios = pwrseq_simple->reset_gpios;
+	unsigned long *values;
+	int nvalues;
+	int ret;
+
+	if (IS_ERR(reset_gpios))
+		return PTR_ERR(reset_gpios);
+
+	nvalues = reset_gpios->ndescs;
+
+	values = bitmap_alloc(nvalues, GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	if (value)
+		bitmap_fill(values, nvalues);
+	else
+		bitmap_zero(values, nvalues);
+
+	ret = gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+				       reset_gpios->info, values);
+	kfree(values);
+
+	return ret;
+}
+
+static int pwrseq_simple_pre_power_on(struct pwrseq *pwrseq)
+{
+	struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+
+	if (!IS_ERR(pwrseq_simple->ext_clk) && !pwrseq_simple->clk_enabled) {
+		clk_prepare_enable(pwrseq_simple->ext_clk);
+		pwrseq_simple->clk_enabled = true;
+	}
+
+	return pwrseq_simple_set_gpios_value(pwrseq_simple, 1);
+}
+
+static int pwrseq_simple_power_on(struct pwrseq *pwrseq)
+{
+	struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+	int ret;
+
+	ret = pwrseq_simple_set_gpios_value(pwrseq_simple, 0);
+	if (ret)
+		return ret;
+
+	if (pwrseq_simple->post_power_on_delay_ms)
+		msleep(pwrseq_simple->post_power_on_delay_ms);
+
+	return 0;
+}
+
+static void pwrseq_simple_power_off(struct pwrseq *pwrseq)
+{
+	struct pwrseq_simple *pwrseq_simple = pwrseq_get_data(pwrseq);
+
+	pwrseq_simple_set_gpios_value(pwrseq_simple, 1);
+
+	if (pwrseq_simple->power_off_delay_us)
+		usleep_range(pwrseq_simple->power_off_delay_us,
+			2 * pwrseq_simple->power_off_delay_us);
+
+	if (!IS_ERR(pwrseq_simple->ext_clk) && pwrseq_simple->clk_enabled) {
+		clk_disable_unprepare(pwrseq_simple->ext_clk);
+		pwrseq_simple->clk_enabled = false;
+	}
+}
+
+static const struct pwrseq_ops pwrseq_simple_ops = {
+	.pre_power_on = pwrseq_simple_pre_power_on,
+	.power_on = pwrseq_simple_power_on,
+	.power_off = pwrseq_simple_power_off,
+};
+
+static const struct of_device_id pwrseq_simple_of_match[] = {
+	{ .compatible = "mmc-pwrseq-simple",}, /* MMC-specific compatible */
+	{/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, pwrseq_simple_of_match);
+
+static int pwrseq_simple_probe(struct platform_device *pdev)
+{
+	struct pwrseq_simple *pwrseq_simple;
+	struct pwrseq *pwrseq;
+	struct pwrseq_provider *provider;
+	struct device *dev = &pdev->dev;
+
+	pwrseq_simple = devm_kzalloc(dev, sizeof(*pwrseq_simple), GFP_KERNEL);
+	if (!pwrseq_simple)
+		return -ENOMEM;
+
+	pwrseq_simple->ext_clk = devm_clk_get(dev, "ext_clock");
+	if (IS_ERR(pwrseq_simple->ext_clk) && PTR_ERR(pwrseq_simple->ext_clk) != -ENOENT)
+		return PTR_ERR(pwrseq_simple->ext_clk);
+
+	pwrseq_simple->reset_gpios = devm_gpiod_get_array(dev, "reset",
+							GPIOD_OUT_HIGH);
+	if (IS_ERR(pwrseq_simple->reset_gpios) &&
+	    PTR_ERR(pwrseq_simple->reset_gpios) != -ENOENT &&
+	    PTR_ERR(pwrseq_simple->reset_gpios) != -ENOSYS) {
+		return PTR_ERR(pwrseq_simple->reset_gpios);
+	}
+
+	device_property_read_u32(dev, "post-power-on-delay-ms",
+				 &pwrseq_simple->post_power_on_delay_ms);
+	device_property_read_u32(dev, "power-off-delay-us",
+				 &pwrseq_simple->power_off_delay_us);
+
+	pwrseq = devm_pwrseq_create(dev, &pwrseq_simple_ops, pwrseq_simple);
+	if (IS_ERR(pwrseq))
+		return PTR_ERR(pwrseq);
+
+	provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_single, pwrseq);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver pwrseq_simple_driver = {
+	.probe = pwrseq_simple_probe,
+	.driver = {
+		.name = "pwrseq_simple",
+		.of_match_table = pwrseq_simple_of_match,
+	},
+};
+
+module_platform_driver(pwrseq_simple_driver);
+MODULE_LICENSE("GPL v2");
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 03/15] mmc: core: switch to new pwrseq subsystem
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 01/15] power: add power sequencer subsystem Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 02/15] pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 04/15] ath10k: add support for pwrseq sequencing Dmitry Baryshkov
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Drop old MMC pwrseq code and use new pwrseq subsystem instead.
Individual drivers are already ported to new subsystem.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/mmc/core/Makefile |   1 -
 drivers/mmc/core/core.c   |   9 ++-
 drivers/mmc/core/host.c   |   8 ++-
 drivers/mmc/core/mmc.c    |   3 +-
 drivers/mmc/core/pwrseq.c | 117 --------------------------------------
 drivers/mmc/core/pwrseq.h |  58 -------------------
 include/linux/mmc/host.h  |   4 +-
 7 files changed, 12 insertions(+), 188 deletions(-)
 delete mode 100644 drivers/mmc/core/pwrseq.c
 delete mode 100644 drivers/mmc/core/pwrseq.h

diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 322eb69bd00e..a504d873cf8e 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -9,7 +9,6 @@ mmc_core-y			:= core.o bus.o host.o \
 				   sdio.o sdio_ops.o sdio_bus.o \
 				   sdio_cis.o sdio_io.o sdio_irq.o \
 				   slot-gpio.o regulator.o
-mmc_core-$(CONFIG_OF)		+= pwrseq.o
 mmc_core-$(CONFIG_DEBUG_FS)	+= debugfs.o
 obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 mmc_block-objs			:= block.o queue.o
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 95fedcf56e4a..c468af900a45 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -41,7 +41,6 @@
 #include "bus.h"
 #include "host.h"
 #include "sdio_bus.h"
-#include "pwrseq.h"
 
 #include "mmc_ops.h"
 #include "sd_ops.h"
@@ -1321,7 +1320,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
 	if (host->ios.power_mode == MMC_POWER_ON)
 		return;
 
-	mmc_pwrseq_pre_power_on(host);
+	pwrseq_pre_power_on(host->pwrseq);
 
 	host->ios.vdd = fls(ocr) - 1;
 	host->ios.power_mode = MMC_POWER_UP;
@@ -1336,7 +1335,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
 	 */
 	mmc_delay(host->ios.power_delay_ms);
 
-	mmc_pwrseq_post_power_on(host);
+	pwrseq_power_on(host->pwrseq);
 
 	host->ios.clock = host->f_init;
 
@@ -1355,7 +1354,7 @@ void mmc_power_off(struct mmc_host *host)
 	if (host->ios.power_mode == MMC_POWER_OFF)
 		return;
 
-	mmc_pwrseq_power_off(host);
+	pwrseq_power_off(host->pwrseq);
 
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
@@ -1985,7 +1984,7 @@ EXPORT_SYMBOL(mmc_set_blocklen);
 
 static void mmc_hw_reset_for_init(struct mmc_host *host)
 {
-	mmc_pwrseq_reset(host);
+	pwrseq_reset(host->pwrseq);
 
 	if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
 		return;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 0475d96047c4..214b9cfda723 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -28,7 +28,6 @@
 #include "crypto.h"
 #include "host.h"
 #include "slot-gpio.h"
-#include "pwrseq.h"
 #include "sdio_ops.h"
 
 #define cls_dev_to_mmc_host(d)	container_of(d, struct mmc_host, class_dev)
@@ -413,7 +412,11 @@ int mmc_of_parse(struct mmc_host *host)
 	device_property_read_u32(dev, "post-power-on-delay-ms",
 				 &host->ios.power_delay_ms);
 
-	return mmc_pwrseq_alloc(host);
+	host->pwrseq = devm_pwrseq_get_optional(dev, "mmc");
+	if (IS_ERR(host->pwrseq))
+		return PTR_ERR(host->pwrseq);
+
+	return 0;
 }
 
 EXPORT_SYMBOL(mmc_of_parse);
@@ -632,7 +635,6 @@ EXPORT_SYMBOL(mmc_remove_host);
  */
 void mmc_free_host(struct mmc_host *host)
 {
-	mmc_pwrseq_free(host);
 	put_device(&host->class_dev);
 }
 
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 838726b68ff3..59d0d26bb5c0 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -24,7 +24,6 @@
 #include "mmc_ops.h"
 #include "quirks.h"
 #include "sd_ops.h"
-#include "pwrseq.h"
 
 #define DEFAULT_CMD6_TIMEOUT_MS	500
 #define MIN_CACHE_EN_TIMEOUT_MS 1600
@@ -2220,7 +2219,7 @@ static int _mmc_hw_reset(struct mmc_host *host)
 	} else {
 		/* Do a brute force power cycle */
 		mmc_power_cycle(host, card->ocr);
-		mmc_pwrseq_reset(host);
+		pwrseq_reset(host->pwrseq);
 	}
 	return mmc_init_card(host, card->ocr, card);
 }
diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c
deleted file mode 100644
index ef675f364bf0..000000000000
--- a/drivers/mmc/core/pwrseq.c
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *  Copyright (C) 2014 Linaro Ltd
- *
- * Author: Ulf Hansson <ulf.hansson@linaro.org>
- *
- *  MMC power sequence management
- */
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/of.h>
-
-#include <linux/mmc/host.h>
-
-#include "pwrseq.h"
-
-static DEFINE_MUTEX(pwrseq_list_mutex);
-static LIST_HEAD(pwrseq_list);
-
-int mmc_pwrseq_alloc(struct mmc_host *host)
-{
-	struct device_node *np;
-	struct mmc_pwrseq *p;
-
-	np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
-	if (!np)
-		return 0;
-
-	mutex_lock(&pwrseq_list_mutex);
-	list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
-		if (p->dev->of_node == np) {
-			if (!try_module_get(p->owner))
-				dev_err(host->parent,
-					"increasing module refcount failed\n");
-			else
-				host->pwrseq = p;
-
-			break;
-		}
-	}
-
-	of_node_put(np);
-	mutex_unlock(&pwrseq_list_mutex);
-
-	if (!host->pwrseq)
-		return -EPROBE_DEFER;
-
-	dev_info(host->parent, "allocated mmc-pwrseq\n");
-
-	return 0;
-}
-
-void mmc_pwrseq_pre_power_on(struct mmc_host *host)
-{
-	struct mmc_pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq && pwrseq->ops->pre_power_on)
-		pwrseq->ops->pre_power_on(host);
-}
-
-void mmc_pwrseq_post_power_on(struct mmc_host *host)
-{
-	struct mmc_pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq && pwrseq->ops->post_power_on)
-		pwrseq->ops->post_power_on(host);
-}
-
-void mmc_pwrseq_power_off(struct mmc_host *host)
-{
-	struct mmc_pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq && pwrseq->ops->power_off)
-		pwrseq->ops->power_off(host);
-}
-
-void mmc_pwrseq_reset(struct mmc_host *host)
-{
-	struct mmc_pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq && pwrseq->ops->reset)
-		pwrseq->ops->reset(host);
-}
-
-void mmc_pwrseq_free(struct mmc_host *host)
-{
-	struct mmc_pwrseq *pwrseq = host->pwrseq;
-
-	if (pwrseq) {
-		module_put(pwrseq->owner);
-		host->pwrseq = NULL;
-	}
-}
-
-int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
-{
-	if (!pwrseq || !pwrseq->ops || !pwrseq->dev)
-		return -EINVAL;
-
-	mutex_lock(&pwrseq_list_mutex);
-	list_add(&pwrseq->pwrseq_node, &pwrseq_list);
-	mutex_unlock(&pwrseq_list_mutex);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(mmc_pwrseq_register);
-
-void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq)
-{
-	if (pwrseq) {
-		mutex_lock(&pwrseq_list_mutex);
-		list_del(&pwrseq->pwrseq_node);
-		mutex_unlock(&pwrseq_list_mutex);
-	}
-}
-EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister);
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
deleted file mode 100644
index f3bb103db9ad..000000000000
--- a/drivers/mmc/core/pwrseq.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2014 Linaro Ltd
- *
- * Author: Ulf Hansson <ulf.hansson@linaro.org>
- */
-#ifndef _MMC_CORE_PWRSEQ_H
-#define _MMC_CORE_PWRSEQ_H
-
-#include <linux/types.h>
-
-struct mmc_host;
-struct device;
-struct module;
-
-struct mmc_pwrseq_ops {
-	void (*pre_power_on)(struct mmc_host *host);
-	void (*post_power_on)(struct mmc_host *host);
-	void (*power_off)(struct mmc_host *host);
-	void (*reset)(struct mmc_host *host);
-};
-
-struct mmc_pwrseq {
-	const struct mmc_pwrseq_ops *ops;
-	struct device *dev;
-	struct list_head pwrseq_node;
-	struct module *owner;
-};
-
-#ifdef CONFIG_OF
-
-int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq);
-void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq);
-
-int mmc_pwrseq_alloc(struct mmc_host *host);
-void mmc_pwrseq_pre_power_on(struct mmc_host *host);
-void mmc_pwrseq_post_power_on(struct mmc_host *host);
-void mmc_pwrseq_power_off(struct mmc_host *host);
-void mmc_pwrseq_reset(struct mmc_host *host);
-void mmc_pwrseq_free(struct mmc_host *host);
-
-#else
-
-static inline int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq)
-{
-	return -ENOSYS;
-}
-static inline void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) {}
-static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; }
-static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {}
-static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {}
-static inline void mmc_pwrseq_power_off(struct mmc_host *host) {}
-static inline void mmc_pwrseq_reset(struct mmc_host *host) {}
-static inline void mmc_pwrseq_free(struct mmc_host *host) {}
-
-#endif
-
-#endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0abd47e9ef9b..1673e37f6028 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -16,6 +16,7 @@
 #include <linux/mmc/pm.h>
 #include <linux/dma-direction.h>
 #include <linux/keyslot-manager.h>
+#include <linux/pwrseq/consumer.h>
 
 struct mmc_ios {
 	unsigned int	clock;			/* clock rate */
@@ -278,7 +279,6 @@ struct mmc_context_info {
 };
 
 struct regulator;
-struct mmc_pwrseq;
 
 struct mmc_supply {
 	struct regulator *vmmc;		/* Card power supply */
@@ -294,7 +294,7 @@ struct mmc_host {
 	struct device		class_dev;
 	int			index;
 	const struct mmc_host_ops *ops;
-	struct mmc_pwrseq	*pwrseq;
+	struct pwrseq		*pwrseq;
 	unsigned int		f_min;
 	unsigned int		f_max;
 	unsigned int		f_init;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 04/15] ath10k: add support for pwrseq sequencing
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (2 preceding siblings ...)
  2021-08-17  0:54 ` [RFC PATCH 03/15] mmc: core: switch " Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 05/15] Bluetooth: hci_qca: merge qca_power into qca_serdev Dmitry Baryshkov
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Power sequencing for Qualcomm WiFi+BT chipsets are being reworked to use
pwrseq rather than individually handling all the regulators. Add support
for pwrseq to ath10k SNOC driver.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/net/wireless/ath/ath10k/snoc.c | 63 ++++++++++++++++++++------
 drivers/net/wireless/ath/ath10k/snoc.h |  2 +
 2 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index ea00fbb15601..870ed28d01a6 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -14,6 +14,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/of_address.h>
 #include <linux/iommu.h>
+#include <linux/pwrseq/consumer.h>
 
 #include "ce.h"
 #include "coredump.h"
@@ -42,7 +43,6 @@ static char *const ce_name[] = {
 };
 
 static const char * const ath10k_regulators[] = {
-	"vdd-0.8-cx-mx",
 	"vdd-1.8-xo",
 	"vdd-1.3-rfa",
 	"vdd-3.3-ch0",
@@ -1010,10 +1010,17 @@ static int ath10k_hw_power_on(struct ath10k *ar)
 
 	ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
 
-	ret = regulator_bulk_enable(ar_snoc->num_vregs, ar_snoc->vregs);
+	if (ar_snoc->pwrseq)
+		ret = pwrseq_full_power_on(ar_snoc->pwrseq);
+	else
+		ret = regulator_bulk_enable(ar_snoc->num_vregs, ar_snoc->vregs);
 	if (ret)
 		return ret;
 
+	ret = regulator_enable(ar_snoc->vreg_cx_mx);
+	if (ret)
+		goto vreg_bulk_off;
+
 	ret = clk_bulk_prepare_enable(ar_snoc->num_clks, ar_snoc->clks);
 	if (ret)
 		goto vreg_off;
@@ -1021,7 +1028,13 @@ static int ath10k_hw_power_on(struct ath10k *ar)
 	return ret;
 
 vreg_off:
-	regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
+	regulator_disable(ar_snoc->vreg_cx_mx);
+vreg_bulk_off:
+	if (ar_snoc->pwrseq)
+		pwrseq_power_off(ar_snoc->pwrseq);
+	else
+		regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
+
 	return ret;
 }
 
@@ -1033,6 +1046,14 @@ static int ath10k_hw_power_off(struct ath10k *ar)
 
 	clk_bulk_disable_unprepare(ar_snoc->num_clks, ar_snoc->clks);
 
+	regulator_disable(ar_snoc->vreg_cx_mx);
+
+	if (ar_snoc->pwrseq) {
+		pwrseq_power_off(ar_snoc->pwrseq);
+
+		return 0;
+	}
+
 	return regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
 }
 
@@ -1691,20 +1712,36 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
 		goto err_release_resource;
 	}
 
-	ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
-	ar_snoc->vregs = devm_kcalloc(&pdev->dev, ar_snoc->num_vregs,
-				      sizeof(*ar_snoc->vregs), GFP_KERNEL);
-	if (!ar_snoc->vregs) {
-		ret = -ENOMEM;
+	ar_snoc->pwrseq = devm_pwrseq_get_optional(&pdev->dev, "wifi");
+	if (IS_ERR(ar_snoc->pwrseq)) {
+		ret = PTR_ERR(ar_snoc->pwrseq);
+		if (ret != -EPROBE_DEFER)
+			ath10k_warn(ar, "failed to acquire pwrseq: %d\n", ret);
 		goto err_free_irq;
 	}
-	for (i = 0; i < ar_snoc->num_vregs; i++)
-		ar_snoc->vregs[i].supply = ath10k_regulators[i];
 
-	ret = devm_regulator_bulk_get(&pdev->dev, ar_snoc->num_vregs,
-				      ar_snoc->vregs);
-	if (ret < 0)
+	if (!ar_snoc->pwrseq) {
+		ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
+		ar_snoc->vregs = devm_kcalloc(&pdev->dev, ar_snoc->num_vregs,
+				sizeof(*ar_snoc->vregs), GFP_KERNEL);
+		if (!ar_snoc->vregs) {
+			ret = -ENOMEM;
+			goto err_free_irq;
+		}
+		for (i = 0; i < ar_snoc->num_vregs; i++)
+			ar_snoc->vregs[i].supply = ath10k_regulators[i];
+
+		ret = devm_regulator_bulk_get(&pdev->dev, ar_snoc->num_vregs,
+				ar_snoc->vregs);
+		if (ret < 0)
+			goto err_free_irq;
+	}
+
+	ar_snoc->vreg_cx_mx = devm_regulator_get(&pdev->dev, "vdd-0.8-cx-mx");
+	if (IS_ERR(ar_snoc->vreg_cx_mx)) {
+		ret = PTR_ERR(ar_snoc->vreg_cx_mx);
 		goto err_free_irq;
+	}
 
 	ar_snoc->num_clks = ARRAY_SIZE(ath10k_clocks);
 	ar_snoc->clks = devm_kcalloc(&pdev->dev, ar_snoc->num_clks,
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index 5095d1893681..16cfd72180cd 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -72,8 +72,10 @@ struct ath10k_snoc {
 	struct timer_list rx_post_retry;
 	struct regulator_bulk_data *vregs;
 	size_t num_vregs;
+	struct regulator *vreg_cx_mx;
 	struct clk_bulk_data *clks;
 	size_t num_clks;
+	struct pwrseq *pwrseq;
 	struct ath10k_qmi *qmi;
 	unsigned long flags;
 	bool xo_cal_supported;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 05/15] Bluetooth: hci_qca: merge qca_power into qca_serdev
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (3 preceding siblings ...)
  2021-08-17  0:54 ` [RFC PATCH 04/15] ath10k: add support for pwrseq sequencing Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 06/15] Bluetooth: hci_qca: merge init paths Dmitry Baryshkov
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

There is no need to separate struct qca_power from the rest of struct
qca_serdev. Squash qca_power into the main struct, to simplify the
driver.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/bluetooth/hci_qca.c | 60 +++++++++++++------------------------
 1 file changed, 20 insertions(+), 40 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 53deea2eb7b4..a21cec44720a 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -205,23 +205,15 @@ struct qca_device_data {
 	uint32_t capabilities;
 };
 
-/*
- * Platform data for the QCA Bluetooth power driver.
- */
-struct qca_power {
-	struct device *dev;
-	struct regulator_bulk_data *vreg_bulk;
-	int num_vregs;
-	bool vregs_on;
-};
-
 struct qca_serdev {
 	struct hci_uart	 serdev_hu;
 	struct gpio_desc *bt_en;
 	struct gpio_desc *sw_ctrl;
 	struct clk	 *susclk;
 	enum qca_btsoc_type btsoc_type;
-	struct qca_power *bt_power;
+	struct regulator_bulk_data *vreg_bulk;
+	int num_vregs;
+	bool vregs_on;
 	u32 init_speed;
 	u32 oper_speed;
 	const char *firmware_name;
@@ -1602,7 +1594,7 @@ static int qca_regulator_init(struct hci_uart *hu)
 	 * off the voltage regulator.
 	 */
 	qcadev = serdev_device_get_drvdata(hu->serdev);
-	if (!qcadev->bt_power->vregs_on) {
+	if (!qcadev->vregs_on) {
 		serdev_device_close(hu->serdev);
 		ret = qca_regulator_enable(qcadev);
 		if (ret)
@@ -1940,20 +1932,19 @@ static int qca_power_off(struct hci_dev *hdev)
 
 static int qca_regulator_enable(struct qca_serdev *qcadev)
 {
-	struct qca_power *power = qcadev->bt_power;
 	int ret;
 
 	/* Already enabled */
-	if (power->vregs_on)
+	if (qcadev->vregs_on)
 		return 0;
 
-	BT_DBG("enabling %d regulators)", power->num_vregs);
+	BT_DBG("enabling %d regulators)", qcadev->num_vregs);
 
-	ret = regulator_bulk_enable(power->num_vregs, power->vreg_bulk);
+	ret = regulator_bulk_enable(qcadev->num_vregs, qcadev->vreg_bulk);
 	if (ret)
 		return ret;
 
-	power->vregs_on = true;
+	qcadev->vregs_on = true;
 
 	ret = clk_prepare_enable(qcadev->susclk);
 	if (ret)
@@ -1964,38 +1955,34 @@ static int qca_regulator_enable(struct qca_serdev *qcadev)
 
 static void qca_regulator_disable(struct qca_serdev *qcadev)
 {
-	struct qca_power *power;
-
 	if (!qcadev)
 		return;
 
-	power = qcadev->bt_power;
-
 	/* Already disabled? */
-	if (!power->vregs_on)
+	if (!qcadev->vregs_on)
 		return;
 
-	regulator_bulk_disable(power->num_vregs, power->vreg_bulk);
-	power->vregs_on = false;
+	regulator_bulk_disable(qcadev->num_vregs, qcadev->vreg_bulk);
+	qcadev->vregs_on = false;
 
 	clk_disable_unprepare(qcadev->susclk);
 }
 
-static int qca_init_regulators(struct qca_power *qca,
+static int qca_init_regulators(struct qca_serdev *qcadev, struct device *dev,
 				const struct qca_vreg *vregs, size_t num_vregs)
 {
 	struct regulator_bulk_data *bulk;
 	int ret;
 	int i;
 
-	bulk = devm_kcalloc(qca->dev, num_vregs, sizeof(*bulk), GFP_KERNEL);
+	bulk = devm_kcalloc(dev, num_vregs, sizeof(*bulk), GFP_KERNEL);
 	if (!bulk)
 		return -ENOMEM;
 
 	for (i = 0; i < num_vregs; i++)
 		bulk[i].supply = vregs[i].name;
 
-	ret = devm_regulator_bulk_get(qca->dev, num_vregs, bulk);
+	ret = devm_regulator_bulk_get(dev, num_vregs, bulk);
 	if (ret < 0)
 		return ret;
 
@@ -2005,8 +1992,8 @@ static int qca_init_regulators(struct qca_power *qca,
 			return ret;
 	}
 
-	qca->vreg_bulk = bulk;
-	qca->num_vregs = num_vregs;
+	qcadev->vreg_bulk = bulk;
+	qcadev->num_vregs = num_vregs;
 
 	return 0;
 }
@@ -2037,21 +2024,15 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 	    (qca_is_wcn399x(data->soc_type) ||
 	    qca_is_wcn6750(data->soc_type))) {
 		qcadev->btsoc_type = data->soc_type;
-		qcadev->bt_power = devm_kzalloc(&serdev->dev,
-						sizeof(struct qca_power),
-						GFP_KERNEL);
-		if (!qcadev->bt_power)
-			return -ENOMEM;
-
-		qcadev->bt_power->dev = &serdev->dev;
-		err = qca_init_regulators(qcadev->bt_power, data->vregs,
+
+		err = qca_init_regulators(qcadev, &serdev->dev, data->vregs,
 					  data->num_vregs);
 		if (err) {
 			BT_ERR("Failed to init regulators:%d", err);
 			return err;
 		}
 
-		qcadev->bt_power->vregs_on = false;
+		qcadev->vregs_on = false;
 
 		qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
 					       GPIOD_OUT_LOW);
@@ -2135,11 +2116,10 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 static void qca_serdev_remove(struct serdev_device *serdev)
 {
 	struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
-	struct qca_power *power = qcadev->bt_power;
 
 	if ((qca_is_wcn399x(qcadev->btsoc_type) ||
 	     qca_is_wcn6750(qcadev->btsoc_type)) &&
-	     power->vregs_on)
+	     qcadev->vregs_on)
 		qca_power_shutdown(&qcadev->serdev_hu);
 	else if (qcadev->susclk)
 		clk_disable_unprepare(qcadev->susclk);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 06/15] Bluetooth: hci_qca: merge init paths
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (4 preceding siblings ...)
  2021-08-17  0:54 ` [RFC PATCH 05/15] Bluetooth: hci_qca: merge qca_power into qca_serdev Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:54 ` [RFC PATCH 07/15] Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init Dmitry Baryshkov
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

The hci_qca driver has almost identical init paths. Merge them together
to remove duplication in preparation to adding power sequencer support.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/bluetooth/hci_qca.c | 76 ++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 43 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index a21cec44720a..279b802f0952 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -2020,11 +2020,12 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 	if (!qcadev->oper_speed)
 		BT_DBG("UART will pick default operating speed");
 
-	if (data &&
-	    (qca_is_wcn399x(data->soc_type) ||
-	    qca_is_wcn6750(data->soc_type))) {
+	if (data)
 		qcadev->btsoc_type = data->soc_type;
+	else
+		qcadev->btsoc_type = QCA_ROME;
 
+	if (data && data->num_vregs) {
 		err = qca_init_regulators(qcadev, &serdev->dev, data->vregs,
 					  data->num_vregs);
 		if (err) {
@@ -2033,48 +2034,33 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 		}
 
 		qcadev->vregs_on = false;
+	}
 
-		qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
-					       GPIOD_OUT_LOW);
-		if (!qcadev->bt_en && data->soc_type == QCA_WCN6750) {
+	qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
+			GPIOD_OUT_LOW);
+	if (!qcadev->bt_en) {
+		if (qca_is_wcn6750(data->soc_type)) {
 			dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
 			power_ctrl_enabled = false;
-		}
-
-		qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl",
-					       GPIOD_IN);
-		if (!qcadev->sw_ctrl && data->soc_type == QCA_WCN6750)
-			dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
-
-		qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
-		if (IS_ERR(qcadev->susclk)) {
-			dev_err(&serdev->dev, "failed to acquire clk\n");
-			return PTR_ERR(qcadev->susclk);
-		}
-
-		err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
-		if (err) {
-			BT_ERR("wcn3990 serdev registration failed");
-			return err;
-		}
-	} else {
-		if (data)
-			qcadev->btsoc_type = data->soc_type;
-		else
-			qcadev->btsoc_type = QCA_ROME;
-
-		qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
-					       GPIOD_OUT_LOW);
-		if (!qcadev->bt_en) {
+		} else if (!qca_is_wcn399x(data->soc_type)) {
 			dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
 			power_ctrl_enabled = false;
 		}
+	}
 
-		qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
-		if (IS_ERR(qcadev->susclk)) {
-			dev_warn(&serdev->dev, "failed to acquire clk\n");
-			return PTR_ERR(qcadev->susclk);
-		}
+	qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl",
+			GPIOD_IN);
+	if (!qcadev->sw_ctrl && qca_is_wcn6750(data->soc_type))
+		dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
+
+	qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
+	if (IS_ERR(qcadev->susclk)) {
+		dev_err(&serdev->dev, "failed to acquire clk\n");
+		return PTR_ERR(qcadev->susclk);
+	}
+
+	if (!qca_is_wcn399x(data->soc_type) &&
+	    !qca_is_wcn6750(data->soc_type)) {
 		err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
 		if (err)
 			return err;
@@ -2082,13 +2068,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 		err = clk_prepare_enable(qcadev->susclk);
 		if (err)
 			return err;
+	}
 
-		err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
-		if (err) {
-			BT_ERR("Rome serdev registration failed");
+	err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
+	if (err) {
+		BT_ERR("QCA serdev registration failed");
+
+		if (!qca_is_wcn399x(data->soc_type) &&
+		    !qca_is_wcn6750(data->soc_type))
 			clk_disable_unprepare(qcadev->susclk);
-			return err;
-		}
+
+		return err;
 	}
 
 	hdev = qcadev->serdev_hu.hdev;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 07/15] Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (5 preceding siblings ...)
  2021-08-17  0:54 ` [RFC PATCH 06/15] Bluetooth: hci_qca: merge init paths Dmitry Baryshkov
@ 2021-08-17  0:54 ` Dmitry Baryshkov
  2021-08-17  0:55 ` [RFC PATCH 08/15] Bluetooth: hci_qca: futher rework of power on/off handling Dmitry Baryshkov
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:54 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

With wcn6750 support added, regulator-based code was extended with the
bt_en gpio support. Now there is no need to keep regulator and
non-regulator code paths separate. Merge both code paths.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/bluetooth/hci_qca.c | 64 ++++++++++++++-----------------------
 1 file changed, 24 insertions(+), 40 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 279b802f0952..ad7e8cdc94f3 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1583,13 +1583,21 @@ static bool qca_prevent_wake(struct hci_dev *hdev)
 	return !wakeup;
 }
 
-static int qca_regulator_init(struct hci_uart *hu)
+static int qca_power_on(struct hci_dev *hdev)
 {
+	struct hci_uart *hu = hci_get_drvdata(hdev);
 	enum qca_btsoc_type soc_type = qca_soc_type(hu);
 	struct qca_serdev *qcadev;
+	struct qca_data *qca = hu->priv;
 	int ret;
 	bool sw_ctrl_state;
 
+	/* Non-serdev device usually is powered by external power
+	 * and don't need additional action in driver for power on
+	 */
+	if (!hu->serdev)
+		return 0;
+
 	/* Check for vregs status, may be hci down has turned
 	 * off the voltage regulator.
 	 */
@@ -1607,27 +1615,29 @@ static int qca_regulator_init(struct hci_uart *hu)
 		}
 	}
 
-	if (qca_is_wcn399x(soc_type)) {
-		/* Forcefully enable wcn399x to enter in to boot mode. */
-		host_set_baudrate(hu, 2400);
-		ret = qca_send_power_pulse(hu, false);
-		if (ret)
-			return ret;
-	}
-
 	/* For wcn6750 need to enable gpio bt_en */
 	if (qcadev->bt_en) {
 		gpiod_set_value_cansleep(qcadev->bt_en, 0);
 		msleep(50);
 		gpiod_set_value_cansleep(qcadev->bt_en, 1);
-		msleep(50);
+		msleep(150);
 		if (qcadev->sw_ctrl) {
 			sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
 			bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
 		}
 	}
 
-	qca_set_speed(hu, QCA_INIT_SPEED);
+	if (qca_is_wcn399x(soc_type)) {
+		/* Forcefully enable wcn399x to enter in to boot mode. */
+		host_set_baudrate(hu, 2400);
+		ret = qca_send_power_pulse(hu, false);
+		if (ret)
+			return ret;
+	}
+
+	if (qca_is_wcn399x(soc_type) ||
+	    qca_is_wcn6750(soc_type))
+		qca_set_speed(hu, QCA_INIT_SPEED);
 
 	if (qca_is_wcn399x(soc_type)) {
 		ret = qca_send_power_pulse(hu, true);
@@ -1647,38 +1657,12 @@ static int qca_regulator_init(struct hci_uart *hu)
 		return ret;
 	}
 
-	hci_uart_set_flow_control(hu, false);
-
-	return 0;
-}
-
-static int qca_power_on(struct hci_dev *hdev)
-{
-	struct hci_uart *hu = hci_get_drvdata(hdev);
-	enum qca_btsoc_type soc_type = qca_soc_type(hu);
-	struct qca_serdev *qcadev;
-	struct qca_data *qca = hu->priv;
-	int ret = 0;
-
-	/* Non-serdev device usually is powered by external power
-	 * and don't need additional action in driver for power on
-	 */
-	if (!hu->serdev)
-		return 0;
-
 	if (qca_is_wcn399x(soc_type) ||
-	    qca_is_wcn6750(soc_type)) {
-		ret = qca_regulator_init(hu);
-	} else {
-		qcadev = serdev_device_get_drvdata(hu->serdev);
-		if (qcadev->bt_en) {
-			gpiod_set_value_cansleep(qcadev->bt_en, 1);
-			/* Controller needs time to bootup. */
-			msleep(150);
-		}
-	}
+	    qca_is_wcn6750(soc_type))
+		hci_uart_set_flow_control(hu, false);
 
 	clear_bit(QCA_BT_OFF, &qca->flags);
+
 	return ret;
 }
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 08/15] Bluetooth: hci_qca: futher rework of power on/off handling
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (6 preceding siblings ...)
  2021-08-17  0:54 ` [RFC PATCH 07/15] Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-17  0:55 ` [RFC PATCH 09/15] Bluetooth: hci_qca: add support for pwrseq Dmitry Baryshkov
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Further rework of power on/off handling. Move bt_en and sw_ctl handling
close to regulator enable/disable, so that this code can be easily
extended with pwrseq support.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/bluetooth/hci_qca.c | 126 +++++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 60 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index ad7e8cdc94f3..2d23e8a3ca14 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1590,7 +1590,6 @@ static int qca_power_on(struct hci_dev *hdev)
 	struct qca_serdev *qcadev;
 	struct qca_data *qca = hu->priv;
 	int ret;
-	bool sw_ctrl_state;
 
 	/* Non-serdev device usually is powered by external power
 	 * and don't need additional action in driver for power on
@@ -1602,29 +1601,15 @@ static int qca_power_on(struct hci_dev *hdev)
 	 * off the voltage regulator.
 	 */
 	qcadev = serdev_device_get_drvdata(hu->serdev);
-	if (!qcadev->vregs_on) {
-		serdev_device_close(hu->serdev);
-		ret = qca_regulator_enable(qcadev);
-		if (ret)
-			return ret;
-
-		ret = serdev_device_open(hu->serdev);
-		if (ret) {
-			bt_dev_err(hu->hdev, "failed to open port");
-			return ret;
-		}
-	}
+	serdev_device_close(hu->serdev);
+	ret = qca_regulator_enable(qcadev);
+	if (ret)
+		return ret;
 
-	/* For wcn6750 need to enable gpio bt_en */
-	if (qcadev->bt_en) {
-		gpiod_set_value_cansleep(qcadev->bt_en, 0);
-		msleep(50);
-		gpiod_set_value_cansleep(qcadev->bt_en, 1);
-		msleep(150);
-		if (qcadev->sw_ctrl) {
-			sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
-			bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
-		}
+	ret = serdev_device_open(hu->serdev);
+	if (ret) {
+		bt_dev_err(hu->hdev, "failed to open port");
+		return ret;
 	}
 
 	if (qca_is_wcn399x(soc_type)) {
@@ -1856,7 +1841,6 @@ static void qca_power_shutdown(struct hci_uart *hu)
 	struct qca_data *qca = hu->priv;
 	unsigned long flags;
 	enum qca_btsoc_type soc_type = qca_soc_type(hu);
-	bool sw_ctrl_state;
 
 	/* From this point we go into power off state. But serial port is
 	 * still open, stop queueing the IBS data and flush all the buffered
@@ -1878,18 +1862,8 @@ static void qca_power_shutdown(struct hci_uart *hu)
 	if (qca_is_wcn399x(soc_type)) {
 		host_set_baudrate(hu, 2400);
 		qca_send_power_pulse(hu, false);
-		qca_regulator_disable(qcadev);
-	} else if (soc_type == QCA_WCN6750) {
-		gpiod_set_value_cansleep(qcadev->bt_en, 0);
-		msleep(100);
-		qca_regulator_disable(qcadev);
-		if (qcadev->sw_ctrl) {
-			sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
-			bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
-		}
-	} else if (qcadev->bt_en) {
-		gpiod_set_value_cansleep(qcadev->bt_en, 0);
 	}
+	qca_regulator_disable(qcadev);
 
 	set_bit(QCA_BT_OFF, &qca->flags);
 }
@@ -1919,20 +1893,39 @@ static int qca_regulator_enable(struct qca_serdev *qcadev)
 	int ret;
 
 	/* Already enabled */
-	if (qcadev->vregs_on)
-		return 0;
+	if (!qcadev->vregs_on) {
+		BT_DBG("enabling %d regulators)", qcadev->num_vregs);
 
-	BT_DBG("enabling %d regulators)", qcadev->num_vregs);
+		ret = regulator_bulk_enable(qcadev->num_vregs, qcadev->vreg_bulk);
+		if (ret)
+			return ret;
 
-	ret = regulator_bulk_enable(qcadev->num_vregs, qcadev->vreg_bulk);
-	if (ret)
-		return ret;
+		qcadev->vregs_on = true;
 
-	qcadev->vregs_on = true;
+		if (qca_is_wcn399x(qcadev->btsoc_type) ||
+		    qca_is_wcn6750(qcadev->btsoc_type)) {
+			ret = clk_prepare_enable(qcadev->susclk);
+			if (ret) {
+				regulator_bulk_disable(qcadev->num_vregs, qcadev->vreg_bulk);
+				return ret;
+			}
+		}
+	}
 
-	ret = clk_prepare_enable(qcadev->susclk);
-	if (ret)
-		qca_regulator_disable(qcadev);
+	/* For wcn6750 need to enable gpio bt_en */
+	if (qcadev->bt_en) {
+		gpiod_set_value_cansleep(qcadev->bt_en, 0);
+		msleep(50);
+		gpiod_set_value_cansleep(qcadev->bt_en, 1);
+		msleep(150);
+	}
+
+	if (qcadev->sw_ctrl) {
+		bool sw_ctrl_state;
+
+		sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
+		bt_dev_dbg(qcadev->serdev_hu.hdev, "SW_CTRL is %d", sw_ctrl_state);
+	}
 
 	return ret;
 }
@@ -1942,14 +1935,27 @@ static void qca_regulator_disable(struct qca_serdev *qcadev)
 	if (!qcadev)
 		return;
 
+	if (qcadev->bt_en) {
+		gpiod_set_value_cansleep(qcadev->bt_en, 0);
+		msleep(100);
+	}
+
 	/* Already disabled? */
-	if (!qcadev->vregs_on)
-		return;
+	if (qcadev->vregs_on) {
+		regulator_bulk_disable(qcadev->num_vregs, qcadev->vreg_bulk);
+		qcadev->vregs_on = false;
 
-	regulator_bulk_disable(qcadev->num_vregs, qcadev->vreg_bulk);
-	qcadev->vregs_on = false;
+		if (qca_is_wcn399x(qcadev->btsoc_type) ||
+		    qca_is_wcn6750(qcadev->btsoc_type))
+			clk_disable_unprepare(qcadev->susclk);
+	}
+
+	if (qcadev->sw_ctrl) {
+		bool sw_ctrl_state;
 
-	clk_disable_unprepare(qcadev->susclk);
+		sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
+		bt_dev_dbg(qcadev->serdev_hu.hdev, "SW_CTRL is %d", sw_ctrl_state);
+	}
 }
 
 static int qca_init_regulators(struct qca_serdev *qcadev, struct device *dev,
@@ -2018,17 +2024,17 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 		}
 
 		qcadev->vregs_on = false;
-	}
 
-	qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
-			GPIOD_OUT_LOW);
-	if (!qcadev->bt_en) {
-		if (qca_is_wcn6750(data->soc_type)) {
-			dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
-			power_ctrl_enabled = false;
-		} else if (!qca_is_wcn399x(data->soc_type)) {
-			dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
-			power_ctrl_enabled = false;
+		qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
+				GPIOD_OUT_LOW);
+		if (!qcadev->bt_en) {
+			if (qca_is_wcn6750(data->soc_type)) {
+				dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
+				power_ctrl_enabled = false;
+			} else if (!qca_is_wcn399x(data->soc_type)) {
+				dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
+				power_ctrl_enabled = false;
+			}
 		}
 	}
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 09/15] Bluetooth: hci_qca: add support for pwrseq
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (7 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 08/15] Bluetooth: hci_qca: futher rework of power on/off handling Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-17  0:55 ` [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer Dmitry Baryshkov
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Support using pwrseq for powering up and down the QCA BT chips.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/bluetooth/hci_qca.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 2d23e8a3ca14..a908a952c0ef 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -31,6 +31,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/serdev.h>
 #include <linux/mutex.h>
+#include <linux/pwrseq/consumer.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -211,6 +212,7 @@ struct qca_serdev {
 	struct gpio_desc *sw_ctrl;
 	struct clk	 *susclk;
 	enum qca_btsoc_type btsoc_type;
+	struct pwrseq *pwrseq;
 	struct regulator_bulk_data *vreg_bulk;
 	int num_vregs;
 	bool vregs_on;
@@ -1602,7 +1604,10 @@ static int qca_power_on(struct hci_dev *hdev)
 	 */
 	qcadev = serdev_device_get_drvdata(hu->serdev);
 	serdev_device_close(hu->serdev);
-	ret = qca_regulator_enable(qcadev);
+	if (qcadev->pwrseq)
+		ret = pwrseq_full_power_on(qcadev->pwrseq);
+	else
+		ret = qca_regulator_enable(qcadev);
 	if (ret)
 		return ret;
 
@@ -1863,7 +1868,10 @@ static void qca_power_shutdown(struct hci_uart *hu)
 		host_set_baudrate(hu, 2400);
 		qca_send_power_pulse(hu, false);
 	}
-	qca_regulator_disable(qcadev);
+	if (qcadev->pwrseq)
+		pwrseq_power_off(qcadev->pwrseq);
+	else
+		qca_regulator_disable(qcadev);
 
 	set_bit(QCA_BT_OFF, &qca->flags);
 }
@@ -2015,7 +2023,15 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 	else
 		qcadev->btsoc_type = QCA_ROME;
 
-	if (data && data->num_vregs) {
+	qcadev->pwrseq = devm_pwrseq_get_optional(&serdev->dev, "bt");
+	if (IS_ERR(qcadev->pwrseq)) {
+		err = PTR_ERR(qcadev->pwrseq);
+		BT_ERR("Failed to init regulators:%d", err);
+		return err;
+
+	}
+
+	if (!qcadev->pwrseq && data && data->num_vregs) {
 		err = qca_init_regulators(qcadev, &serdev->dev, data->vregs,
 					  data->num_vregs);
 		if (err) {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (8 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 09/15] Bluetooth: hci_qca: add support for pwrseq Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-19 23:18   ` Bjorn Andersson
  2021-08-17  0:55 ` [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca " Dmitry Baryshkov
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Add support for power sequencer used in the Qualcomm BT+WiFi SoCs. They
require several external volate regulators and some of them use separate
BT and WiFi enable GPIO pins. This code is mostly extracted from the
hci_qca.c bluetooth driver and ath10k WiFi driver. Instead of having
each of them manage pins, different requirements, regulator types, move
this knowledge to the common power sequencer driver. Currently original
drivers are not stripped from the regulator code, this will be done
later (to keep compatibility with the old device trees).

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/power/pwrseq/Kconfig      |  14 ++
 drivers/power/pwrseq/Makefile     |   1 +
 drivers/power/pwrseq/pwrseq_qca.c | 290 ++++++++++++++++++++++++++++++
 3 files changed, 305 insertions(+)
 create mode 100644 drivers/power/pwrseq/pwrseq_qca.c

diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
index 36339a456b03..990c3164144e 100644
--- a/drivers/power/pwrseq/Kconfig
+++ b/drivers/power/pwrseq/Kconfig
@@ -19,6 +19,20 @@ config PWRSEQ_EMMC
 	  This driver can also be built as a module. If so, the module
 	  will be called pwrseq_emmc.
 
+config PWRSEQ_QCA
+	tristate "Power Sequencer for Qualcomm WiFi + BT SoCs"
+	depends on OF
+	help
+	  If you say yes to this option, support will be included for Qualcomm
+	  WCN399x,QCA639x,QCA67xx families of hybrid WiFi and Bluetooth SoCs.
+	  Note, this driver supports only power control for these SoC, you
+	  still have to enable individual Bluetooth and WiFi drivers. This
+	  driver is only necessary on ARM platforms with these chips. PCIe
+	  cards handle power sequencing on their own.
+
+	  Say M here if you want to include support for Qualcomm WiFi+BT SoCs
+	  as a module. This will build a module called "pwrseq_qca".
+
 config PWRSEQ_SD8787
 	tristate "HW reset support for SD8787 BT + Wifi module"
 	depends on OF
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
index 6f359d228843..556bf5582d47 100644
--- a/drivers/power/pwrseq/Makefile
+++ b/drivers/power/pwrseq/Makefile
@@ -6,5 +6,6 @@
 obj-$(CONFIG_PWRSEQ) += core.o
 
 obj-$(CONFIG_PWRSEQ_EMMC)	+= pwrseq_emmc.o
+obj-$(CONFIG_PWRSEQ_QCA)	+= pwrseq_qca.o
 obj-$(CONFIG_PWRSEQ_SD8787)	+= pwrseq_sd8787.o
 obj-$(CONFIG_PWRSEQ_SIMPLE)	+= pwrseq_simple.o
diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
new file mode 100644
index 000000000000..3421a4821126
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_qca.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Linaro Ltd.
+ *
+ * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+ *
+ * Power Sequencer for Qualcomm WiFi + BT SoCs
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwrseq/driver.h>
+#include <linux/regulator/consumer.h>
+
+/*
+ * Voltage regulator information required for configuring the
+ * QCA WiFi+Bluetooth chipset
+ */
+struct qca_vreg {
+	const char *name;
+	unsigned int load_uA;
+};
+
+struct qca_device_data {
+	struct qca_vreg vddio;
+	struct qca_vreg *vregs;
+	size_t num_vregs;
+	bool has_bt_en;
+	bool has_wifi_en;
+};
+
+struct pwrseq_qca;
+struct pwrseq_qca_one {
+	struct pwrseq_qca *common;
+	struct gpio_desc *enable;
+};
+
+#define PWRSEQ_QCA_WIFI 0
+#define PWRSEQ_QCA_BT 1
+
+#define PWRSEQ_QCA_MAX 2
+
+struct pwrseq_qca {
+	struct regulator *vddio;
+	struct gpio_desc *sw_ctrl;
+	struct pwrseq_qca_one pwrseq_qcas[PWRSEQ_QCA_MAX];
+	int num_vregs;
+	struct regulator_bulk_data vregs[];
+};
+
+static int pwrseq_qca_power_on(struct pwrseq *pwrseq)
+{
+	struct pwrseq_qca_one *qca_one = pwrseq_get_data(pwrseq);
+	int ret;
+
+	if (qca_one->common->vddio) {
+		ret = regulator_enable(qca_one->common->vddio);
+		if (ret)
+			return ret;
+	}
+
+	ret = regulator_bulk_enable(qca_one->common->num_vregs, qca_one->common->vregs);
+	if (ret)
+		goto vddio_off;
+
+	if (qca_one->enable) {
+		gpiod_set_value_cansleep(qca_one->enable, 0);
+		msleep(50);
+		gpiod_set_value_cansleep(qca_one->enable, 1);
+		msleep(150);
+	}
+
+	if (qca_one->common->sw_ctrl) {
+		bool sw_ctrl_state = gpiod_get_value_cansleep(qca_one->common->sw_ctrl);
+		dev_dbg(&pwrseq->dev, "SW_CTRL is %d", sw_ctrl_state);
+	}
+
+	return 0;
+
+vddio_off:
+	regulator_disable(qca_one->common->vddio);
+
+	return ret;
+}
+
+static void pwrseq_qca_power_off(struct pwrseq *pwrseq)
+{
+	struct pwrseq_qca_one *qca_one = pwrseq_get_data(pwrseq);
+
+	if (qca_one->enable) {
+		gpiod_set_value_cansleep(qca_one->enable, 0);
+		msleep(50);
+	}
+
+	regulator_bulk_disable(qca_one->common->num_vregs, qca_one->common->vregs);
+	regulator_disable(qca_one->common->vddio);
+
+	if (qca_one->common->sw_ctrl) {
+		bool sw_ctrl_state = gpiod_get_value_cansleep(qca_one->common->sw_ctrl);
+		dev_dbg(&pwrseq->dev, "SW_CTRL is %d", sw_ctrl_state);
+	}
+}
+
+static const struct pwrseq_ops pwrseq_qca_ops = {
+	.power_on = pwrseq_qca_power_on,
+	.power_off = pwrseq_qca_power_off,
+};
+
+static int pwrseq_qca_regulators_init(struct device *dev, struct pwrseq_qca *pwrseq_qca,
+		const struct qca_device_data *data)
+{
+	int ret, i;
+
+	pwrseq_qca->vddio = devm_regulator_get(dev, data->vddio.name);
+	if (IS_ERR(pwrseq_qca->vddio))
+		return PTR_ERR(pwrseq_qca->vddio);
+
+	ret = regulator_set_load(pwrseq_qca->vddio, data->vddio.load_uA);
+	if (ret)
+		return ret;
+
+	pwrseq_qca->num_vregs = data->num_vregs;
+
+	for (i = 0; i < pwrseq_qca->num_vregs; i++)
+		pwrseq_qca->vregs[i].supply = data->vregs[i].name;
+
+	ret = devm_regulator_bulk_get(dev, pwrseq_qca->num_vregs, pwrseq_qca->vregs);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < pwrseq_qca->num_vregs; i++) {
+		if (!data->vregs[i].load_uA)
+			continue;
+
+		ret = regulator_set_load(pwrseq_qca->vregs[i].consumer, data->vregs[i].load_uA);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int pwrseq_qca_probe(struct platform_device *pdev)
+{
+	struct pwrseq_qca *pwrseq_qca;
+	struct pwrseq *pwrseq;
+	struct pwrseq_provider *provider;
+	struct device *dev = &pdev->dev;
+	struct pwrseq_onecell_data *onecell;
+	const struct qca_device_data *data;
+	int ret, i;
+
+	data = device_get_match_data(dev);
+	if (!data)
+		return -EINVAL;
+
+	pwrseq_qca = devm_kzalloc(dev, struct_size(pwrseq_qca, vregs, data->num_vregs), GFP_KERNEL);
+	if (!pwrseq_qca)
+		return -ENOMEM;
+
+	onecell = devm_kzalloc(dev, struct_size(onecell, pwrseqs, PWRSEQ_QCA_MAX), GFP_KERNEL);
+	if (!onecell)
+		return -ENOMEM;
+
+	ret = pwrseq_qca_regulators_init(dev, pwrseq_qca, data);
+	if (ret)
+		return ret;
+
+	if (data->has_wifi_en) {
+		pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable = devm_gpiod_get(dev, "wifi-enable", GPIOD_OUT_LOW);
+		if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable)) {
+			return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable),
+					"failed to acquire WIFI enable GPIO\n");
+		}
+	}
+
+	if (data->has_bt_en) {
+		pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable = devm_gpiod_get(dev, "bt-enable", GPIOD_OUT_LOW);
+		if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable)) {
+			return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable),
+					"failed to acquire BT enable GPIO\n");
+		}
+	}
+
+	pwrseq_qca->sw_ctrl = devm_gpiod_get_optional(dev, "swctrl", GPIOD_IN);
+	if (IS_ERR(pwrseq_qca->sw_ctrl)) {
+		return dev_err_probe(dev, PTR_ERR(pwrseq_qca->sw_ctrl),
+				"failed to acquire SW_CTRL gpio\n");
+	} else if (!pwrseq_qca->sw_ctrl)
+		dev_info(dev, "No SW_CTRL gpio\n");
+
+	for (i = 0; i < PWRSEQ_QCA_MAX; i++) {
+		pwrseq_qca->pwrseq_qcas[i].common = pwrseq_qca;
+
+		pwrseq = devm_pwrseq_create(dev, &pwrseq_qca_ops, &pwrseq_qca->pwrseq_qcas[i]);
+		if (IS_ERR(pwrseq))
+			return PTR_ERR(pwrseq);
+
+		onecell->pwrseqs[i] = pwrseq;
+	}
+
+	onecell->num = PWRSEQ_QCA_MAX;
+
+	provider = devm_of_pwrseq_provider_register(dev, of_pwrseq_xlate_onecell, onecell);
+
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct qca_device_data qca_soc_data_qca6390 = {
+	.vddio = { "vddio", 20000 },
+	.vregs = (struct qca_vreg []) {
+		/* 2.0 V */
+		{ "vddpcie2", 15000 },
+		{ "vddrfa3", 400000 },
+
+		/* 0.95 V */
+		{ "vddaon", 100000 },
+		{ "vddpmu", 1250000 },
+		{ "vddrfa1", 200000 },
+
+		/* 1.35 V */
+		{ "vddrfa2", 400000 },
+		{ "vddpcie1", 35000 },
+	},
+	.num_vregs = 7,
+	.has_bt_en = true,
+	.has_wifi_en = true,
+};
+
+/* Shared between wcn3990 and wcn3991 */
+static const struct qca_device_data qca_soc_data_wcn3990 = {
+	.vddio = { "vddio", 15000 },
+	.vregs = (struct qca_vreg []) {
+		{ "vddxo", 80000  },
+		{ "vddrfa", 300000 },
+		{ "vddch0", 450000 },
+		{ "vddch1", 450000 },
+	},
+	.num_vregs = 4,
+};
+
+static const struct qca_device_data qca_soc_data_wcn3998 = {
+	.vddio = { "vddio", 10000 },
+	.vregs = (struct qca_vreg []) {
+		{ "vddxo", 80000  },
+		{ "vddrfa", 300000 },
+		{ "vddch0", 450000 },
+		{ "vddch1", 450000 },
+	},
+	.num_vregs = 4,
+};
+
+static const struct qca_device_data qca_soc_data_wcn6750 = {
+	.vddio = { "vddio", 5000 },
+	.vregs = (struct qca_vreg []) {
+		{ "vddaon", 26000 },
+		{ "vddbtcxmx", 126000 },
+		{ "vddrfacmn", 12500 },
+		{ "vddrfa0p8", 102000 },
+		{ "vddrfa1p7", 302000 },
+		{ "vddrfa1p2", 257000 },
+		{ "vddrfa2p2", 1700000 },
+		{ "vddasd", 200 },
+	},
+	.num_vregs = 8,
+	.has_bt_en = true,
+	.has_wifi_en = true,
+};
+
+static const struct of_device_id pwrseq_qca_of_match[] = {
+	{ .compatible = "qcom,qca6390-pwrseq", .data = &qca_soc_data_qca6390 },
+	{ .compatible = "qcom,wcn3990-pwrseq", .data = &qca_soc_data_wcn3990 },
+	{ .compatible = "qcom,wcn3991-pwrseq", .data = &qca_soc_data_wcn3990 },
+	{ .compatible = "qcom,wcn3998-pwrseq", .data = &qca_soc_data_wcn3998 },
+	{ .compatible = "qcom,wcn6750-pwrseq", .data = &qca_soc_data_wcn6750 },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, pwrseq_qca_of_match);
+
+static struct platform_driver pwrseq_qca_driver = {
+	.probe = pwrseq_qca_probe,
+	.driver = {
+		.name = "pwrseq_qca",
+		.of_match_table = pwrseq_qca_of_match,
+	},
+};
+
+module_platform_driver(pwrseq_qca_driver);
+MODULE_LICENSE("GPL v2");
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca power sequencer
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (9 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-19 23:40   ` Bjorn Andersson
  2021-08-17  0:55 ` [RFC PATCH 12/15] arm64: dts: qcom: qrb5165-rb5: add bluetooth support Dmitry Baryshkov
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Switch sdm845-db845c device tree to use new power sequencer driver
rather than separate regulators.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 21 ++++++++++++++-------
 arch/arm64/boot/dts/qcom/sdm845.dtsi       |  6 ++++++
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
index 2d5533dd4ec2..c9b694e934d4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
@@ -629,6 +629,16 @@ &qupv3_id_1 {
 	status = "okay";
 };
 
+&qca_pwrseq {
+	status = "okay";
+
+	vddio-supply = <&vreg_s4a_1p8>;
+
+	vddxo-supply = <&vreg_l7a_1p8>;
+	vddrfa-supply = <&vreg_l17a_1p3>;
+	vddch0-supply = <&vreg_l25a_3p3>;
+};
+
 &sdhc_2 {
 	status = "okay";
 
@@ -916,10 +926,8 @@ &uart6 {
 	bluetooth {
 		compatible = "qcom,wcn3990-bt";
 
-		vddio-supply = <&vreg_s4a_1p8>;
-		vddxo-supply = <&vreg_l7a_1p8>;
-		vddrf-supply = <&vreg_l17a_1p3>;
-		vddch0-supply = <&vreg_l25a_3p3>;
+		bt-pwrseq = <&qca_pwrseq 1>;
+
 		max-speed = <3200000>;
 	};
 };
@@ -1036,9 +1044,8 @@ &wifi {
 	status = "okay";
 
 	vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>;
-	vdd-1.8-xo-supply = <&vreg_l7a_1p8>;
-	vdd-1.3-rfa-supply = <&vreg_l17a_1p3>;
-	vdd-3.3-ch0-supply = <&vreg_l25a_3p3>;
+
+	wifi-pwrseq = <&qca_pwrseq 0>;
 
 	qcom,snoc-host-cap-8bit-quirk;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 0a86fe71a66d..78e889b2c8dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1051,6 +1051,12 @@ psci {
 		method = "smc";
 	};
 
+	qca_pwrseq: qca-pwrseq {
+		compatible = "qcom,wcn3990-pwrseq";
+		#pwrseq-cells = <1>;
+		status = "disabled";
+	};
+
 	soc: soc@0 {
 		#address-cells = <2>;
 		#size-cells = <2>;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 12/15] arm64: dts: qcom: qrb5165-rb5: add bluetooth support
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (10 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca " Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-17  0:55 ` [RFC PATCH 13/15] arm64: dts: qcom: sdm845-db845c: add second channel support to qca power sequencer Dmitry Baryshkov
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Add support for the bluetooth part of the QCA6391 BT+WiFi chip present
on the RB5 board. WiFi is not supported yet, as it requires separate
handling of the PCIe device power.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 50 ++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index 8ac96f8e79d4..326330f528fc 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -19,6 +19,7 @@ / {
 
 	aliases {
 		serial0 = &uart12;
+		serial1 = &uart6;
 		sdhc2 = &sdhc_2;
 	};
 
@@ -98,6 +99,25 @@ lt9611_3v3: lt9611-3v3 {
 		regulator-always-on;
 	};
 
+	qca_pwrseq: qca-pwrseq {
+		compatible = "qcom,qca6390-pwrseq";
+
+		#pwrseq-cells = <1>;
+
+		vddaon-supply = <&vreg_s6a_0p95>;
+		vddpmu-supply = <&vreg_s2f_0p95>;
+		vddrfa1-supply = <&vreg_s2f_0p95>;
+		vddrfa2-supply = <&vreg_s8c_1p3>;
+		vddrfa3-supply = <&vreg_s5a_1p9>;
+		vddpcie1-supply = <&vreg_s8c_1p3>;
+		vddpcie2-supply = <&vreg_s5a_1p9>;
+		vddio-supply = <&vreg_s4a_1p8>;
+
+		bt-enable-gpios = <&tlmm 21 GPIO_ACTIVE_HIGH>;
+		wifi-enable-gpios = <&tlmm 20 GPIO_ACTIVE_HIGH>;
+		swctrl-gpios = <&tlmm 124 GPIO_ACTIVE_HIGH>;
+	};
+
 	thermal-zones {
 		conn-thermal {
 			polling-delay-passive = <0>;
@@ -804,6 +824,26 @@ lt9611_rst_pin: lt9611-rst-pin {
 	};
 };
 
+&qup_uart6_default {
+	ctsrx {
+		pins = "gpio16", "gpio19";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
+	rts {
+		pins = "gpio17";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
+	tx {
+		pins = "gpio18";
+		drive-strength = <2>;
+		bias-pull-up;
+	};
+};
+
 &qupv3_id_0 {
 	status = "okay";
 };
@@ -1193,6 +1233,16 @@ sdc2_card_det_n: sd-card-det-n {
 	};
 };
 
+&uart6 {
+	status = "okay";
+	bluetooth {
+		compatible = "qcom,qca6390-bt";
+		clocks = <&sleep_clk>;
+
+		bt-pwrseq = <&qca_pwrseq 1>;
+	};
+};
+
 &uart12 {
 	status = "okay";
 };
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 13/15] arm64: dts: qcom: sdm845-db845c: add second channel support to qca power sequencer
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (11 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 12/15] arm64: dts: qcom: qrb5165-rb5: add bluetooth support Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-17  0:55 ` [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices Dmitry Baryshkov
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
index c9b694e934d4..3025e5efd556 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
@@ -342,6 +342,12 @@ vreg_l21a_2p95: ldo21 {
 			regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
 		};
 
+		vreg_l23a_3p3: ldo23 {
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3312000>;
+			regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+		};
+
 		vreg_l24a_3p075: ldo24 {
 			regulator-min-microvolt = <3088000>;
 			regulator-max-microvolt = <3088000>;
@@ -637,6 +643,7 @@ &qca_pwrseq {
 	vddxo-supply = <&vreg_l7a_1p8>;
 	vddrfa-supply = <&vreg_l17a_1p3>;
 	vddch0-supply = <&vreg_l25a_3p3>;
+	vddch1-supply = <&vreg_l23a_3p3>;
 };
 
 &sdhc_2 {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (12 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 13/15] arm64: dts: qcom: sdm845-db845c: add second channel support to qca power sequencer Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-19 23:44   ` Bjorn Andersson
  2021-08-17  0:55 ` [RFC PATCH 15/15] WIP: arm64: dts: qcom: qrb5165-rb5: add bus-pwrseq property to pcie0 Dmitry Baryshkov
  2021-08-19 15:23 ` [RFC PATCH 00/15] create power sequencing subsystem Marcel Holtmann
  15 siblings, 1 reply; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Use bus-pwrseq device tree node to power up the devices on the bus. This
is to be rewritten with the proper code parsing the device tree and
powering up individual devices.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 drivers/pci/controller/dwc/pcie-qcom.c | 13 +++++++++++++
 drivers/power/pwrseq/pwrseq_qca.c      |  1 +
 2 files changed, 14 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 8a7a300163e5..a60d41fbcd6f 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -23,6 +23,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
+#include <linux/pwrseq/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
@@ -1467,6 +1468,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
 	struct pcie_port *pp;
 	struct dw_pcie *pci;
 	struct qcom_pcie *pcie;
+	struct pwrseq *pwrseq;
 	int ret;
 
 	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
@@ -1520,6 +1522,17 @@ static int qcom_pcie_probe(struct platform_device *pdev)
 
 	pp->ops = &qcom_pcie_dw_ops;
 
+	pwrseq = devm_pwrseq_get_optional(dev, "bus");
+	if (IS_ERR(pwrseq)) {
+		ret = PTR_ERR(pwrseq);
+		goto err_pm_runtime_put;
+	}
+	if (pwrseq) {
+		ret = pwrseq_full_power_on(pwrseq);
+		if (ret)
+			goto err_pm_runtime_put;
+	}
+
 	ret = phy_init(pcie->phy);
 	if (ret) {
 		pm_runtime_disable(&pdev->dev);
diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
index 3421a4821126..4107f0a9c05d 100644
--- a/drivers/power/pwrseq/pwrseq_qca.c
+++ b/drivers/power/pwrseq/pwrseq_qca.c
@@ -1,3 +1,4 @@
+#define DEBUG
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2021, Linaro Ltd.
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [RFC PATCH 15/15] WIP: arm64: dts: qcom: qrb5165-rb5: add bus-pwrseq property to pcie0
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (13 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices Dmitry Baryshkov
@ 2021-08-17  0:55 ` Dmitry Baryshkov
  2021-08-19 15:23 ` [RFC PATCH 00/15] create power sequencing subsystem Marcel Holtmann
  15 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-17  0:55 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Ulf Hansson, Marcel Holtmann,
	Johan Hedberg, Luiz Augusto von Dentz, Kalle Valo,
	David S. Miller, Jakub Kicinski, Stanimir Varbanov
  Cc: linux-arm-msm, linux-mmc, linux-kernel, linux-bluetooth, ath10k,
	linux-wireless, netdev

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
 arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index 326330f528fc..0c347cb6f8e0 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -689,6 +689,7 @@ wifi-therm@1 {
 
 &pcie0 {
 	status = "okay";
+	bus-pwrseq = <&qca_pwrseq 0>;
 };
 
 &pcie0_phy {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 00/15] create power sequencing subsystem
  2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
                   ` (14 preceding siblings ...)
  2021-08-17  0:55 ` [RFC PATCH 15/15] WIP: arm64: dts: qcom: qrb5165-rb5: add bus-pwrseq property to pcie0 Dmitry Baryshkov
@ 2021-08-19 15:23 ` Marcel Holtmann
  2021-08-20 13:08   ` Dmitry Baryshkov
  15 siblings, 1 reply; 28+ messages in thread
From: Marcel Holtmann @ 2021-08-19 15:23 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Bjorn Andersson, Ulf Hansson, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, MSM, linux-mmc, open list,
	open list:BLUETOOTH SUBSYSTEM, ath10k, linux-wireless, netdev

Hi Dmitry,

> This is an RFC of the proposed power sequencer subsystem. This is a
> generification of the MMC pwrseq code. The subsystem tries to abstract
> the idea of complex power-up/power-down/reset of the devices.
> 
> The primary set of devices that promted me to create this patchset is
> the Qualcomm BT+WiFi family of chips. They reside on serial+platform
> interfaces (older generations) or on serial+PCIe (newer generations).
> They require a set of external voltage regulators to be powered on and
> (some of them) have separate WiFi and Bluetooth enable GPIOs.
> 
> This patchset being an RFC tries to demonstrate the approach, design and
> usage of the pwrseq subsystem. Following issues are present in the RFC
> at this moment but will be fixed later if the overall approach would be
> viewed as acceptable:
> 
> - No documentation
>   While the code tries to be self-documenting proper documentation
>   would be required.
> 
> - Minimal device tree bindings changes
>   There are no proper updates for the DT bindings (thus neither Rob
>   Herring nor devicetree are included in the To/Cc lists). The dt
>   schema changes would be a part of v1.
> 
> - Lack of proper PCIe integration
>   At this moment support for PCIe is hacked up to be able to test the
>   PCIe part of qca6390. Proper PCIe support would require automatically
>   powering up the devices before the scan basing on the proper device
>   structure in the device tree.
> 
> ----------------------------------------------------------------
> Dmitry Baryshkov (15):
>      power: add power sequencer subsystem
>      pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
>      mmc: core: switch to new pwrseq subsystem
>      ath10k: add support for pwrseq sequencing
>      Bluetooth: hci_qca: merge qca_power into qca_serdev
>      Bluetooth: hci_qca: merge init paths
>      Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
>      Bluetooth: hci_qca: futher rework of power on/off handling
>      Bluetooth: hci_qca: add support for pwrseq

any chance you can try to abandon patching hci_qca. The serdev support in hci_uart is rather hacking into old line discipline code and it is not aging well. It is really becoming a mess.

I would say that the Qualcomm serial devices could use a separate standalone serdev driver. A while I send an RFC for a new serdev driver.

https://www.spinics.net/lists/linux-bluetooth/msg74918.html

There I had the idea that simple vendor specifics can be in that driver (like the Broadcom part I added there), but frankly the QCA specifics are a bit too specific and it should be a separate driver. However I think this would be a good starting point.

In general a H:4 based Bluetooth driver is dead simple with the help of h4_recv.h helper we have in the kernel. The complicated part is the power management pieces or any vendor specific low-power protocol they are running on that serial line. And since you are touching this anyway, doing a driver from scratch might be lot simpler and cleaner. It would surely help all the new QCA device showing up in the future.

Regards

Marcel


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer
  2021-08-17  0:55 ` [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer Dmitry Baryshkov
@ 2021-08-19 23:18   ` Bjorn Andersson
  2021-08-20  8:10     ` Dmitry Baryshkov
  0 siblings, 1 reply; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-19 23:18 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, linux-arm-msm, linux-mmc,
	linux-kernel, linux-bluetooth, ath10k, linux-wireless, netdev

On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:
[..]
> diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
> new file mode 100644
> index 000000000000..3421a4821126
> --- /dev/null
> +++ b/drivers/power/pwrseq/pwrseq_qca.c
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021, Linaro Ltd.
> + *
> + * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> + *
> + * Power Sequencer for Qualcomm WiFi + BT SoCs
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwrseq/driver.h>
> +#include <linux/regulator/consumer.h>
> +
> +/*
> + * Voltage regulator information required for configuring the
> + * QCA WiFi+Bluetooth chipset
> + */
> +struct qca_vreg {
> +	const char *name;
> +	unsigned int load_uA;
> +};
> +
> +struct qca_device_data {
> +	struct qca_vreg vddio;

Any particular reason why this isn't just the first entry in vregs and
operated as part of the bulk API?

> +	struct qca_vreg *vregs;
> +	size_t num_vregs;
> +	bool has_bt_en;
> +	bool has_wifi_en;
> +};
> +
> +struct pwrseq_qca;
> +struct pwrseq_qca_one {
> +	struct pwrseq_qca *common;
> +	struct gpio_desc *enable;
> +};
> +
> +#define PWRSEQ_QCA_WIFI 0
> +#define PWRSEQ_QCA_BT 1
> +
> +#define PWRSEQ_QCA_MAX 2
> +
> +struct pwrseq_qca {
> +	struct regulator *vddio;
> +	struct gpio_desc *sw_ctrl;
> +	struct pwrseq_qca_one pwrseq_qcas[PWRSEQ_QCA_MAX];
> +	int num_vregs;
> +	struct regulator_bulk_data vregs[];
> +};
> +
> +static int pwrseq_qca_power_on(struct pwrseq *pwrseq)
> +{
> +	struct pwrseq_qca_one *qca_one = pwrseq_get_data(pwrseq);
> +	int ret;
> +
> +	if (qca_one->common->vddio) {

devm_regulator_get() doesn't return NULL, so this is always true.

> +		ret = regulator_enable(qca_one->common->vddio);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regulator_bulk_enable(qca_one->common->num_vregs, qca_one->common->vregs);
> +	if (ret)
> +		goto vddio_off;
> +
> +	if (qca_one->enable) {
> +		gpiod_set_value_cansleep(qca_one->enable, 0);
> +		msleep(50);
> +		gpiod_set_value_cansleep(qca_one->enable, 1);
> +		msleep(150);
> +	}
> +
> +	if (qca_one->common->sw_ctrl) {
> +		bool sw_ctrl_state = gpiod_get_value_cansleep(qca_one->common->sw_ctrl);
> +		dev_dbg(&pwrseq->dev, "SW_CTRL is %d", sw_ctrl_state);
> +	}
> +
> +	return 0;
> +
> +vddio_off:
> +	regulator_disable(qca_one->common->vddio);
> +
> +	return ret;
> +}
[..]
> +static int pwrseq_qca_probe(struct platform_device *pdev)
> +{
> +	struct pwrseq_qca *pwrseq_qca;
> +	struct pwrseq *pwrseq;
> +	struct pwrseq_provider *provider;
> +	struct device *dev = &pdev->dev;
> +	struct pwrseq_onecell_data *onecell;
> +	const struct qca_device_data *data;
> +	int ret, i;
> +
> +	data = device_get_match_data(dev);
> +	if (!data)
> +		return -EINVAL;
> +
> +	pwrseq_qca = devm_kzalloc(dev, struct_size(pwrseq_qca, vregs, data->num_vregs), GFP_KERNEL);
> +	if (!pwrseq_qca)
> +		return -ENOMEM;
> +
> +	onecell = devm_kzalloc(dev, struct_size(onecell, pwrseqs, PWRSEQ_QCA_MAX), GFP_KERNEL);
> +	if (!onecell)
> +		return -ENOMEM;
> +
> +	ret = pwrseq_qca_regulators_init(dev, pwrseq_qca, data);
> +	if (ret)
> +		return ret;
> +
> +	if (data->has_wifi_en) {
> +		pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable = devm_gpiod_get(dev, "wifi-enable", GPIOD_OUT_LOW);
> +		if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable)) {
> +			return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable),
> +					"failed to acquire WIFI enable GPIO\n");
> +		}
> +	}
> +
> +	if (data->has_bt_en) {
> +		pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable = devm_gpiod_get(dev, "bt-enable", GPIOD_OUT_LOW);
> +		if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable)) {
> +			return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable),
> +					"failed to acquire BT enable GPIO\n");
> +		}
> +	}
> +
> +	pwrseq_qca->sw_ctrl = devm_gpiod_get_optional(dev, "swctrl", GPIOD_IN);
> +	if (IS_ERR(pwrseq_qca->sw_ctrl)) {
> +		return dev_err_probe(dev, PTR_ERR(pwrseq_qca->sw_ctrl),
> +				"failed to acquire SW_CTRL gpio\n");
> +	} else if (!pwrseq_qca->sw_ctrl)
> +		dev_info(dev, "No SW_CTRL gpio\n");

Some {} around the else as well please.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 01/15] power: add power sequencer subsystem
  2021-08-17  0:54 ` [RFC PATCH 01/15] power: add power sequencer subsystem Dmitry Baryshkov
@ 2021-08-19 23:37   ` Bjorn Andersson
  0 siblings, 0 replies; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-19 23:37 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, linux-arm-msm, linux-mmc,
	linux-kernel, linux-bluetooth, ath10k, linux-wireless, netdev

On Mon 16 Aug 17:54 PDT 2021, Dmitry Baryshkov wrote:

> Basing on MMC's pwrseq support code, add separate power sequencer
> subsystem. It will be used by other drivers to handle device power up
> requirements.
> 

Some more background to why we need a pwrseq framework wouldn't hurt.

[..]
> diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
> new file mode 100644
> index 000000000000..20485cae29aa
> --- /dev/null
> +++ b/drivers/power/pwrseq/core.c
> @@ -0,0 +1,411 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//
> +// Copyright 2021 (c) Linaro Ltd.
> +// Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> +//
> +// Based on phy-core.c:
> +// Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com

The typical format is:

// SPDX using C++ style comment
/*
 * Copyright stuff using C style comment
 */

> +
> +#include <linux/device.h>
> +#include <linux/idr.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pwrseq/consumer.h>
> +#include <linux/pwrseq/driver.h>
> +#include <linux/slab.h>
> +
> +#define	to_pwrseq(a)	(container_of((a), struct pwrseq, dev))

No need for the extra parenthesis around container_of()

> +
> +static DEFINE_IDA(pwrseq_ida);
> +static DEFINE_MUTEX(pwrseq_provider_mutex);
> +static LIST_HEAD(pwrseq_provider_list);
> +
> +struct pwrseq_provider {
> +	struct device		*dev;
> +	struct module		*owner;
> +	struct list_head	list;
> +	void			*data;
> +	struct pwrseq * (*of_xlate)(void *data, struct of_phandle_args *args);
> +};
> +
> +void pwrseq_put(struct device *dev, struct pwrseq *pwrseq)
> +{
> +	device_link_remove(dev, &pwrseq->dev);
> +
> +	module_put(pwrseq->owner);
> +	put_device(&pwrseq->dev);
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_put);
> +
> +static struct pwrseq_provider *of_pwrseq_provider_lookup(struct device_node *node)
> +{
> +	struct pwrseq_provider *pwrseq_provider;
> +
> +	list_for_each_entry(pwrseq_provider, &pwrseq_provider_list, list) {
> +		if (pwrseq_provider->dev->of_node == node)
> +			return pwrseq_provider;
> +	}
> +
> +	return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +static struct pwrseq *_of_pwrseq_get(struct device *dev, const char *id)
> +{
> +	struct pwrseq_provider *pwrseq_provider;
> +	struct pwrseq *pwrseq;
> +	struct of_phandle_args args;
> +	char prop_name[64]; /* 64 is max size of property name */
> +	int ret;
> +
> +	snprintf(prop_name, 64, "%s-pwrseq", id);

sizeof(prop_name), to avoid giving others a chance to "fix" it later?

> +	ret = of_parse_phandle_with_args(dev->of_node, prop_name, "#pwrseq-cells", 0, &args);
> +	if (ret) {
> +		struct device_node *dn;
> +
> +		/*
> +		 * Parsing failed. Try locating old bindings for mmc-pwrseq,
> +		 * which did not use #pwrseq-cells.
> +		 */
> +		if (strcmp(id, "mmc"))
> +			return ERR_PTR(-ENODEV);
> +
> +		dn = of_parse_phandle(dev->of_node, prop_name, 0);
> +		if (!dn)
> +			return ERR_PTR(-ENODEV);
> +
> +		args.np = dn;
> +		args.args_count = 0;
> +	}
> +
> +	mutex_lock(&pwrseq_provider_mutex);
> +	pwrseq_provider = of_pwrseq_provider_lookup(args.np);
> +	if (IS_ERR(pwrseq_provider) || !try_module_get(pwrseq_provider->owner)) {
> +		pwrseq = ERR_PTR(-EPROBE_DEFER);
> +		goto out_unlock;
> +	}
> +
> +	if (!of_device_is_available(args.np)) {
> +		dev_warn(pwrseq_provider->dev, "Requested pwrseq is disabled\n");
> +		pwrseq = ERR_PTR(-ENODEV);
> +		goto out_put_module;
> +	}
> +
> +	pwrseq = pwrseq_provider->of_xlate(pwrseq_provider->data, &args);
> +
> +out_put_module:
> +	module_put(pwrseq_provider->owner);
> +
> +out_unlock:
> +	mutex_unlock(&pwrseq_provider_mutex);
> +	of_node_put(args.np);
> +
> +	return pwrseq;
> +}
> +
[..]
> +int pwrseq_pre_power_on(struct pwrseq *pwrseq)
> +{
> +	if (pwrseq && pwrseq->ops->pre_power_on)
> +		return pwrseq->ops->pre_power_on(pwrseq);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_pre_power_on);
> +
> +int pwrseq_power_on(struct pwrseq *pwrseq)

Wouldn't it make sense to refcount the power on/off operations and at
least warn about unbalanced disables?

My concern is related to the qca-driver's reliance on the regulator
framework to refcount the on/off of the shared resources and additional
power_off from either the WiFi or BT client would result in the other
client getting its power disabled unexpectedly - which might be
annoying to debug.

> +{
> +	if (pwrseq && pwrseq->ops->power_on)
> +		return pwrseq->ops->power_on(pwrseq);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_power_on);
> +
> +void pwrseq_power_off(struct pwrseq *pwrseq)
> +{
> +	if (pwrseq && pwrseq->ops->power_off)
> +		pwrseq->ops->power_off(pwrseq);
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_power_off);
> +
> +void pwrseq_reset(struct pwrseq *pwrseq)
> +{
> +	if (pwrseq && pwrseq->ops->reset)
> +		pwrseq->ops->reset(pwrseq);
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_reset);
> +

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca power sequencer
  2021-08-17  0:55 ` [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca " Dmitry Baryshkov
@ 2021-08-19 23:40   ` Bjorn Andersson
  0 siblings, 0 replies; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-19 23:40 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, linux-arm-msm, linux-mmc,
	linux-kernel, linux-bluetooth, ath10k, linux-wireless, netdev

On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:

> Switch sdm845-db845c device tree to use new power sequencer driver
> rather than separate regulators.
> 

LGTM!

> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> ---
>  arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 21 ++++++++++++++-------
>  arch/arm64/boot/dts/qcom/sdm845.dtsi       |  6 ++++++
>  2 files changed, 20 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
> index 2d5533dd4ec2..c9b694e934d4 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
> +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
> @@ -629,6 +629,16 @@ &qupv3_id_1 {
>  	status = "okay";
>  };
>  
> +&qca_pwrseq {
> +	status = "okay";
> +
> +	vddio-supply = <&vreg_s4a_1p8>;
> +
> +	vddxo-supply = <&vreg_l7a_1p8>;
> +	vddrfa-supply = <&vreg_l17a_1p3>;
> +	vddch0-supply = <&vreg_l25a_3p3>;
> +};
> +
>  &sdhc_2 {
>  	status = "okay";
>  
> @@ -916,10 +926,8 @@ &uart6 {
>  	bluetooth {
>  		compatible = "qcom,wcn3990-bt";
>  
> -		vddio-supply = <&vreg_s4a_1p8>;
> -		vddxo-supply = <&vreg_l7a_1p8>;
> -		vddrf-supply = <&vreg_l17a_1p3>;
> -		vddch0-supply = <&vreg_l25a_3p3>;
> +		bt-pwrseq = <&qca_pwrseq 1>;

I suppose there will be a DT binding and some defines for the WiFi/BT
argument to &qca_pwrseq?

Regards,
Bjorn

> +
>  		max-speed = <3200000>;
>  	};
>  };
> @@ -1036,9 +1044,8 @@ &wifi {
>  	status = "okay";
>  
>  	vdd-0.8-cx-mx-supply = <&vreg_l5a_0p8>;
> -	vdd-1.8-xo-supply = <&vreg_l7a_1p8>;
> -	vdd-1.3-rfa-supply = <&vreg_l17a_1p3>;
> -	vdd-3.3-ch0-supply = <&vreg_l25a_3p3>;
> +
> +	wifi-pwrseq = <&qca_pwrseq 0>;
>  
>  	qcom,snoc-host-cap-8bit-quirk;
>  };
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index 0a86fe71a66d..78e889b2c8dd 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -1051,6 +1051,12 @@ psci {
>  		method = "smc";
>  	};
>  
> +	qca_pwrseq: qca-pwrseq {
> +		compatible = "qcom,wcn3990-pwrseq";
> +		#pwrseq-cells = <1>;
> +		status = "disabled";
> +	};
> +
>  	soc: soc@0 {
>  		#address-cells = <2>;
>  		#size-cells = <2>;
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices
  2021-08-17  0:55 ` [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices Dmitry Baryshkov
@ 2021-08-19 23:44   ` Bjorn Andersson
  2021-08-20  8:50     ` Dmitry Baryshkov
  0 siblings, 1 reply; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-19 23:44 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, linux-arm-msm, linux-mmc,
	linux-kernel, linux-bluetooth, ath10k, linux-wireless, netdev

On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:

> Use bus-pwrseq device tree node to power up the devices on the bus. This
> is to be rewritten with the proper code parsing the device tree and
> powering up individual devices.
> 

How about describing the PCI device in DT and having the PCIe controller
dig it up up from there? Although we won't have a struct device until
later, so perhaps we need the of-based pwrseq_get() for that.

Regards,
Bjorn

> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> ---
>  drivers/pci/controller/dwc/pcie-qcom.c | 13 +++++++++++++
>  drivers/power/pwrseq/pwrseq_qca.c      |  1 +
>  2 files changed, 14 insertions(+)
> 
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index 8a7a300163e5..a60d41fbcd6f 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -23,6 +23,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/platform_device.h>
>  #include <linux/phy/phy.h>
> +#include <linux/pwrseq/consumer.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/reset.h>
>  #include <linux/slab.h>
> @@ -1467,6 +1468,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
>  	struct pcie_port *pp;
>  	struct dw_pcie *pci;
>  	struct qcom_pcie *pcie;
> +	struct pwrseq *pwrseq;
>  	int ret;
>  
>  	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> @@ -1520,6 +1522,17 @@ static int qcom_pcie_probe(struct platform_device *pdev)
>  
>  	pp->ops = &qcom_pcie_dw_ops;
>  
> +	pwrseq = devm_pwrseq_get_optional(dev, "bus");
> +	if (IS_ERR(pwrseq)) {
> +		ret = PTR_ERR(pwrseq);
> +		goto err_pm_runtime_put;
> +	}
> +	if (pwrseq) {
> +		ret = pwrseq_full_power_on(pwrseq);
> +		if (ret)
> +			goto err_pm_runtime_put;
> +	}
> +
>  	ret = phy_init(pcie->phy);
>  	if (ret) {
>  		pm_runtime_disable(&pdev->dev);
> diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
> index 3421a4821126..4107f0a9c05d 100644
> --- a/drivers/power/pwrseq/pwrseq_qca.c
> +++ b/drivers/power/pwrseq/pwrseq_qca.c
> @@ -1,3 +1,4 @@
> +#define DEBUG
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
>   * Copyright (c) 2021, Linaro Ltd.
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer
  2021-08-19 23:18   ` Bjorn Andersson
@ 2021-08-20  8:10     ` Dmitry Baryshkov
  2021-08-20 16:35       ` Bjorn Andersson
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-20  8:10 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov,
	open list:DRM DRIVER FOR MSM ADRENO GPU, linux-mmc, open list,
	linux-bluetooth, ath10k, linux-wireless, netdev

Hi,

On Fri, 20 Aug 2021 at 02:17, Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:
> [..]
> > diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
> > new file mode 100644
> > index 000000000000..3421a4821126
> > --- /dev/null
> > +++ b/drivers/power/pwrseq/pwrseq_qca.c
> > @@ -0,0 +1,290 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2021, Linaro Ltd.
> > + *
> > + * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > + *
> > + * Power Sequencer for Qualcomm WiFi + BT SoCs
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwrseq/driver.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +/*
> > + * Voltage regulator information required for configuring the
> > + * QCA WiFi+Bluetooth chipset
> > + */
> > +struct qca_vreg {
> > +     const char *name;
> > +     unsigned int load_uA;
> > +};
> > +
> > +struct qca_device_data {
> > +     struct qca_vreg vddio;
>
> Any particular reason why this isn't just the first entry in vregs and
> operated as part of the bulk API?

Because VDDIO should be up before bringing the rest of the power
sources (at least for wcn39xx). This is usually the case since VDDIO
is S4A, but I'd still prefer to express this in the code.
And register_bulk_enable powers up all the supplies asynchronously,
thus it can not guarantee that the first entry would be powered up
first.

>
> > +     struct qca_vreg *vregs;
> > +     size_t num_vregs;
> > +     bool has_bt_en;
> > +     bool has_wifi_en;
> > +};
> > +
> > +struct pwrseq_qca;
> > +struct pwrseq_qca_one {
> > +     struct pwrseq_qca *common;
> > +     struct gpio_desc *enable;
> > +};
> > +
> > +#define PWRSEQ_QCA_WIFI 0
> > +#define PWRSEQ_QCA_BT 1
> > +
> > +#define PWRSEQ_QCA_MAX 2
> > +
> > +struct pwrseq_qca {
> > +     struct regulator *vddio;
> > +     struct gpio_desc *sw_ctrl;
> > +     struct pwrseq_qca_one pwrseq_qcas[PWRSEQ_QCA_MAX];
> > +     int num_vregs;
> > +     struct regulator_bulk_data vregs[];
> > +};
> > +
> > +static int pwrseq_qca_power_on(struct pwrseq *pwrseq)
> > +{
> > +     struct pwrseq_qca_one *qca_one = pwrseq_get_data(pwrseq);
> > +     int ret;
> > +
> > +     if (qca_one->common->vddio) {
>
> devm_regulator_get() doesn't return NULL, so this is always true.

This is more of the safety guard for the cases when the qca doesn't
have the special vddio supply.

>
> > +             ret = regulator_enable(qca_one->common->vddio);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     ret = regulator_bulk_enable(qca_one->common->num_vregs, qca_one->common->vregs);
> > +     if (ret)
> > +             goto vddio_off;
> > +
> > +     if (qca_one->enable) {
> > +             gpiod_set_value_cansleep(qca_one->enable, 0);
> > +             msleep(50);
> > +             gpiod_set_value_cansleep(qca_one->enable, 1);
> > +             msleep(150);
> > +     }
> > +
> > +     if (qca_one->common->sw_ctrl) {
> > +             bool sw_ctrl_state = gpiod_get_value_cansleep(qca_one->common->sw_ctrl);
> > +             dev_dbg(&pwrseq->dev, "SW_CTRL is %d", sw_ctrl_state);
> > +     }
> > +
> > +     return 0;
> > +
> > +vddio_off:
> > +     regulator_disable(qca_one->common->vddio);
> > +
> > +     return ret;
> > +}
> [..]
> > +static int pwrseq_qca_probe(struct platform_device *pdev)
> > +{
> > +     struct pwrseq_qca *pwrseq_qca;
> > +     struct pwrseq *pwrseq;
> > +     struct pwrseq_provider *provider;
> > +     struct device *dev = &pdev->dev;
> > +     struct pwrseq_onecell_data *onecell;
> > +     const struct qca_device_data *data;
> > +     int ret, i;
> > +
> > +     data = device_get_match_data(dev);
> > +     if (!data)
> > +             return -EINVAL;
> > +
> > +     pwrseq_qca = devm_kzalloc(dev, struct_size(pwrseq_qca, vregs, data->num_vregs), GFP_KERNEL);
> > +     if (!pwrseq_qca)
> > +             return -ENOMEM;
> > +
> > +     onecell = devm_kzalloc(dev, struct_size(onecell, pwrseqs, PWRSEQ_QCA_MAX), GFP_KERNEL);
> > +     if (!onecell)
> > +             return -ENOMEM;
> > +
> > +     ret = pwrseq_qca_regulators_init(dev, pwrseq_qca, data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (data->has_wifi_en) {
> > +             pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable = devm_gpiod_get(dev, "wifi-enable", GPIOD_OUT_LOW);
> > +             if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable)) {
> > +                     return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_WIFI].enable),
> > +                                     "failed to acquire WIFI enable GPIO\n");
> > +             }
> > +     }
> > +
> > +     if (data->has_bt_en) {
> > +             pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable = devm_gpiod_get(dev, "bt-enable", GPIOD_OUT_LOW);
> > +             if (IS_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable)) {
> > +                     return dev_err_probe(dev, PTR_ERR(pwrseq_qca->pwrseq_qcas[PWRSEQ_QCA_BT].enable),
> > +                                     "failed to acquire BT enable GPIO\n");
> > +             }
> > +     }
> > +
> > +     pwrseq_qca->sw_ctrl = devm_gpiod_get_optional(dev, "swctrl", GPIOD_IN);
> > +     if (IS_ERR(pwrseq_qca->sw_ctrl)) {
> > +             return dev_err_probe(dev, PTR_ERR(pwrseq_qca->sw_ctrl),
> > +                             "failed to acquire SW_CTRL gpio\n");
> > +     } else if (!pwrseq_qca->sw_ctrl)
> > +             dev_info(dev, "No SW_CTRL gpio\n");
>
> Some {} around the else as well please.

ack

>
> Regards,
> Bjorn



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices
  2021-08-19 23:44   ` Bjorn Andersson
@ 2021-08-20  8:50     ` Dmitry Baryshkov
  0 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-20  8:50 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov,
	open list:DRM DRIVER FOR MSM ADRENO GPU, linux-mmc, open list,
	linux-bluetooth, ath10k, linux-wireless, netdev

Hi,

On Fri, 20 Aug 2021 at 02:43, Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:
>
> > Use bus-pwrseq device tree node to power up the devices on the bus. This
> > is to be rewritten with the proper code parsing the device tree and
> > powering up individual devices.
> >
>
> How about describing the PCI device in DT and having the PCIe controller
> dig it up up from there? Although we won't have a struct device until
> later, so perhaps we need the of-based pwrseq_get() for that.

Yes, this is the plan. Currently I just wanted to have a way to power
up the PCIe part of the chip and be able to test that.
In the previous attempts to provide qca6390 support, Rob has clearly
stated that we'd have to have the following device tree snippet:

pcie0 {
    bridge@0,0 {
        qca6390@1,0 {
            supplies-or-whatever = ....;
        };
    };
};

>
> Regards,
> Bjorn
>
> > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > ---
> >  drivers/pci/controller/dwc/pcie-qcom.c | 13 +++++++++++++
> >  drivers/power/pwrseq/pwrseq_qca.c      |  1 +
> >  2 files changed, 14 insertions(+)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> > index 8a7a300163e5..a60d41fbcd6f 100644
> > --- a/drivers/pci/controller/dwc/pcie-qcom.c
> > +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> > @@ -23,6 +23,7 @@
> >  #include <linux/pm_runtime.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/phy/phy.h>
> > +#include <linux/pwrseq/consumer.h>
> >  #include <linux/regulator/consumer.h>
> >  #include <linux/reset.h>
> >  #include <linux/slab.h>
> > @@ -1467,6 +1468,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
> >       struct pcie_port *pp;
> >       struct dw_pcie *pci;
> >       struct qcom_pcie *pcie;
> > +     struct pwrseq *pwrseq;
> >       int ret;
> >
> >       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> > @@ -1520,6 +1522,17 @@ static int qcom_pcie_probe(struct platform_device *pdev)
> >
> >       pp->ops = &qcom_pcie_dw_ops;
> >
> > +     pwrseq = devm_pwrseq_get_optional(dev, "bus");
> > +     if (IS_ERR(pwrseq)) {
> > +             ret = PTR_ERR(pwrseq);
> > +             goto err_pm_runtime_put;
> > +     }
> > +     if (pwrseq) {
> > +             ret = pwrseq_full_power_on(pwrseq);
> > +             if (ret)
> > +                     goto err_pm_runtime_put;
> > +     }
> > +
> >       ret = phy_init(pcie->phy);
> >       if (ret) {
> >               pm_runtime_disable(&pdev->dev);
> > diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
> > index 3421a4821126..4107f0a9c05d 100644
> > --- a/drivers/power/pwrseq/pwrseq_qca.c
> > +++ b/drivers/power/pwrseq/pwrseq_qca.c
> > @@ -1,3 +1,4 @@
> > +#define DEBUG
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> >   * Copyright (c) 2021, Linaro Ltd.
> > --
> > 2.30.2
> >



-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 00/15] create power sequencing subsystem
  2021-08-19 15:23 ` [RFC PATCH 00/15] create power sequencing subsystem Marcel Holtmann
@ 2021-08-20 13:08   ` Dmitry Baryshkov
  2021-08-20 17:02     ` Bjorn Andersson
  2021-08-21  6:50     ` Marcel Holtmann
  0 siblings, 2 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-20 13:08 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Andy Gross, Bjorn Andersson, Ulf Hansson, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, MSM, linux-mmc, open list,
	open list:BLUETOOTH SUBSYSTEM, ath10k, linux-wireless, netdev

Hi,

On Thu, 19 Aug 2021 at 18:23, Marcel Holtmann <marcel@holtmann.org> wrote:
> > This is an RFC of the proposed power sequencer subsystem. This is a
> > generification of the MMC pwrseq code. The subsystem tries to abstract
> > the idea of complex power-up/power-down/reset of the devices.
> >
> > The primary set of devices that promted me to create this patchset is
> > the Qualcomm BT+WiFi family of chips. They reside on serial+platform
> > interfaces (older generations) or on serial+PCIe (newer generations).
> > They require a set of external voltage regulators to be powered on and
> > (some of them) have separate WiFi and Bluetooth enable GPIOs.
> >
> > This patchset being an RFC tries to demonstrate the approach, design and
> > usage of the pwrseq subsystem. Following issues are present in the RFC
> > at this moment but will be fixed later if the overall approach would be
> > viewed as acceptable:
> >
> > - No documentation
> >   While the code tries to be self-documenting proper documentation
> >   would be required.
> >
> > - Minimal device tree bindings changes
> >   There are no proper updates for the DT bindings (thus neither Rob
> >   Herring nor devicetree are included in the To/Cc lists). The dt
> >   schema changes would be a part of v1.
> >
> > - Lack of proper PCIe integration
> >   At this moment support for PCIe is hacked up to be able to test the
> >   PCIe part of qca6390. Proper PCIe support would require automatically
> >   powering up the devices before the scan basing on the proper device
> >   structure in the device tree.
> >
> > ----------------------------------------------------------------
> > Dmitry Baryshkov (15):
> >      power: add power sequencer subsystem
> >      pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
> >      mmc: core: switch to new pwrseq subsystem
> >      ath10k: add support for pwrseq sequencing
> >      Bluetooth: hci_qca: merge qca_power into qca_serdev
> >      Bluetooth: hci_qca: merge init paths
> >      Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
> >      Bluetooth: hci_qca: futher rework of power on/off handling
> >      Bluetooth: hci_qca: add support for pwrseq
>
> any chance you can try to abandon patching hci_qca. The serdev support in hci_uart is rather hacking into old line discipline code and it is not aging well. It is really becoming a mess.

I wanted to stay away from rewriting the BT code. But... New driver
would have a bonus point that I don't have to be compatible with old
bindings. In fact we can even make it the other way around: let the
old driver always use regulators and make the new driver support only
the pwrseq. Then it should be possible to drop the old hci_qca driver
together with dropping the old bindings.

> I would say that the Qualcomm serial devices could use a separate standalone serdev driver. A while I send an RFC for a new serdev driver.
>
> https://www.spinics.net/lists/linux-bluetooth/msg74918.html

Any reason why your driver stayed as an RFC and never made it into the
kernel? Do you plan to revive your old RFCs on H:4 and H:5?

> There I had the idea that simple vendor specifics can be in that driver (like the Broadcom part I added there), but frankly the QCA specifics are a bit too specific and it should be a separate driver. However I think this would be a good starting point.
>
> In general a H:4 based Bluetooth driver is dead simple with the help of h4_recv.h helper we have in the kernel. The complicated part is the power management pieces or any vendor specific low-power protocol they are running on that serial line. And since you are touching this anyway, doing a driver from scratch might be lot simpler and cleaner. It would surely help all the new QCA device showing up in the future.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer
  2021-08-20  8:10     ` Dmitry Baryshkov
@ 2021-08-20 16:35       ` Bjorn Andersson
  0 siblings, 0 replies; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-20 16:35 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Ulf Hansson, Marcel Holtmann, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov,
	open list:DRM DRIVER FOR MSM ADRENO GPU, linux-mmc, open list,
	linux-bluetooth, ath10k, linux-wireless, netdev

On Fri 20 Aug 01:10 PDT 2021, Dmitry Baryshkov wrote:

> Hi,
> 
> On Fri, 20 Aug 2021 at 02:17, Bjorn Andersson
> <bjorn.andersson@linaro.org> wrote:
> >
> > On Mon 16 Aug 17:55 PDT 2021, Dmitry Baryshkov wrote:
> > [..]
> > > diff --git a/drivers/power/pwrseq/pwrseq_qca.c b/drivers/power/pwrseq/pwrseq_qca.c
> > > new file mode 100644
> > > index 000000000000..3421a4821126
> > > --- /dev/null
> > > +++ b/drivers/power/pwrseq/pwrseq_qca.c
> > > @@ -0,0 +1,290 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright (c) 2021, Linaro Ltd.
> > > + *
> > > + * Author: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > > + *
> > > + * Power Sequencer for Qualcomm WiFi + BT SoCs
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pwrseq/driver.h>
> > > +#include <linux/regulator/consumer.h>
> > > +
> > > +/*
> > > + * Voltage regulator information required for configuring the
> > > + * QCA WiFi+Bluetooth chipset
> > > + */
> > > +struct qca_vreg {
> > > +     const char *name;
> > > +     unsigned int load_uA;
> > > +};
> > > +
> > > +struct qca_device_data {
> > > +     struct qca_vreg vddio;
> >
> > Any particular reason why this isn't just the first entry in vregs and
> > operated as part of the bulk API?
> 
> Because VDDIO should be up before bringing the rest of the power
> sources (at least for wcn39xx). This is usually the case since VDDIO
> is S4A, but I'd still prefer to express this in the code.
> And register_bulk_enable powers up all the supplies asynchronously,
> thus it can not guarantee that the first entry would be powered up
> first.
> 

Ahh, forgot about the async nature of bulk_enable. Make the code a
little ugly, but it needs to be done like that.

Thinking about it, isn't there a required minimum time between vddio and
the others in the wcn specification?

> >
> > > +     struct qca_vreg *vregs;
> > > +     size_t num_vregs;
> > > +     bool has_bt_en;
> > > +     bool has_wifi_en;
> > > +};
> > > +
> > > +struct pwrseq_qca;
> > > +struct pwrseq_qca_one {
> > > +     struct pwrseq_qca *common;
> > > +     struct gpio_desc *enable;
> > > +};
> > > +
> > > +#define PWRSEQ_QCA_WIFI 0
> > > +#define PWRSEQ_QCA_BT 1
> > > +
> > > +#define PWRSEQ_QCA_MAX 2
> > > +
> > > +struct pwrseq_qca {
> > > +     struct regulator *vddio;
> > > +     struct gpio_desc *sw_ctrl;
> > > +     struct pwrseq_qca_one pwrseq_qcas[PWRSEQ_QCA_MAX];
> > > +     int num_vregs;
> > > +     struct regulator_bulk_data vregs[];
> > > +};
> > > +
> > > +static int pwrseq_qca_power_on(struct pwrseq *pwrseq)
> > > +{
> > > +     struct pwrseq_qca_one *qca_one = pwrseq_get_data(pwrseq);
> > > +     int ret;
> > > +
> > > +     if (qca_one->common->vddio) {
> >
> > devm_regulator_get() doesn't return NULL, so this is always true.
> 
> This is more of the safety guard for the cases when the qca doesn't
> have the special vddio supply.
> 

If you think there's such a case coming up, then it makes sense.
On the flip side, debugging the resulting panic when someone adds a new
compatible without vddio is very minor...


I think this looks good then.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 00/15] create power sequencing subsystem
  2021-08-20 13:08   ` Dmitry Baryshkov
@ 2021-08-20 17:02     ` Bjorn Andersson
  2021-08-20 18:06       ` Dmitry Baryshkov
  2021-08-21  6:50     ` Marcel Holtmann
  1 sibling, 1 reply; 28+ messages in thread
From: Bjorn Andersson @ 2021-08-20 17:02 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Marcel Holtmann, Andy Gross, Ulf Hansson, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, MSM, linux-mmc, open list,
	open list:BLUETOOTH SUBSYSTEM, ath10k, linux-wireless, netdev

On Fri 20 Aug 06:08 PDT 2021, Dmitry Baryshkov wrote:

> Hi,
> 
> On Thu, 19 Aug 2021 at 18:23, Marcel Holtmann <marcel@holtmann.org> wrote:
> > > This is an RFC of the proposed power sequencer subsystem. This is a
> > > generification of the MMC pwrseq code. The subsystem tries to abstract
> > > the idea of complex power-up/power-down/reset of the devices.
> > >
> > > The primary set of devices that promted me to create this patchset is
> > > the Qualcomm BT+WiFi family of chips. They reside on serial+platform
> > > interfaces (older generations) or on serial+PCIe (newer generations).
> > > They require a set of external voltage regulators to be powered on and
> > > (some of them) have separate WiFi and Bluetooth enable GPIOs.
> > >
> > > This patchset being an RFC tries to demonstrate the approach, design and
> > > usage of the pwrseq subsystem. Following issues are present in the RFC
> > > at this moment but will be fixed later if the overall approach would be
> > > viewed as acceptable:
> > >
> > > - No documentation
> > >   While the code tries to be self-documenting proper documentation
> > >   would be required.
> > >
> > > - Minimal device tree bindings changes
> > >   There are no proper updates for the DT bindings (thus neither Rob
> > >   Herring nor devicetree are included in the To/Cc lists). The dt
> > >   schema changes would be a part of v1.
> > >
> > > - Lack of proper PCIe integration
> > >   At this moment support for PCIe is hacked up to be able to test the
> > >   PCIe part of qca6390. Proper PCIe support would require automatically
> > >   powering up the devices before the scan basing on the proper device
> > >   structure in the device tree.
> > >
> > > ----------------------------------------------------------------
> > > Dmitry Baryshkov (15):
> > >      power: add power sequencer subsystem
> > >      pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
> > >      mmc: core: switch to new pwrseq subsystem
> > >      ath10k: add support for pwrseq sequencing
> > >      Bluetooth: hci_qca: merge qca_power into qca_serdev
> > >      Bluetooth: hci_qca: merge init paths
> > >      Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
> > >      Bluetooth: hci_qca: futher rework of power on/off handling
> > >      Bluetooth: hci_qca: add support for pwrseq
> >
> > any chance you can try to abandon patching hci_qca. The serdev support in hci_uart is rather hacking into old line discipline code and it is not aging well. It is really becoming a mess.
> 
> I wanted to stay away from rewriting the BT code. But... New driver
> would have a bonus point that I don't have to be compatible with old
> bindings.

It would be preferable if this was a implementation-only change and that
we kept the existing binding and existing dtb continued to work.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 00/15] create power sequencing subsystem
  2021-08-20 17:02     ` Bjorn Andersson
@ 2021-08-20 18:06       ` Dmitry Baryshkov
  0 siblings, 0 replies; 28+ messages in thread
From: Dmitry Baryshkov @ 2021-08-20 18:06 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Marcel Holtmann, Andy Gross, Ulf Hansson, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, MSM, linux-mmc, open list,
	open list:BLUETOOTH SUBSYSTEM, ath10k, linux-wireless, netdev

On Fri, 20 Aug 2021 at 20:01, Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Fri 20 Aug 06:08 PDT 2021, Dmitry Baryshkov wrote:
>
> > Hi,
> >
> > On Thu, 19 Aug 2021 at 18:23, Marcel Holtmann <marcel@holtmann.org> wrote:
> > > > This is an RFC of the proposed power sequencer subsystem. This is a
> > > > generification of the MMC pwrseq code. The subsystem tries to abstract
> > > > the idea of complex power-up/power-down/reset of the devices.
> > > >
> > > > The primary set of devices that promted me to create this patchset is
> > > > the Qualcomm BT+WiFi family of chips. They reside on serial+platform
> > > > interfaces (older generations) or on serial+PCIe (newer generations).
> > > > They require a set of external voltage regulators to be powered on and
> > > > (some of them) have separate WiFi and Bluetooth enable GPIOs.
> > > >
> > > > This patchset being an RFC tries to demonstrate the approach, design and
> > > > usage of the pwrseq subsystem. Following issues are present in the RFC
> > > > at this moment but will be fixed later if the overall approach would be
> > > > viewed as acceptable:
> > > >
> > > > - No documentation
> > > >   While the code tries to be self-documenting proper documentation
> > > >   would be required.
> > > >
> > > > - Minimal device tree bindings changes
> > > >   There are no proper updates for the DT bindings (thus neither Rob
> > > >   Herring nor devicetree are included in the To/Cc lists). The dt
> > > >   schema changes would be a part of v1.
> > > >
> > > > - Lack of proper PCIe integration
> > > >   At this moment support for PCIe is hacked up to be able to test the
> > > >   PCIe part of qca6390. Proper PCIe support would require automatically
> > > >   powering up the devices before the scan basing on the proper device
> > > >   structure in the device tree.
> > > >
> > > > ----------------------------------------------------------------
> > > > Dmitry Baryshkov (15):
> > > >      power: add power sequencer subsystem
> > > >      pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
> > > >      mmc: core: switch to new pwrseq subsystem
> > > >      ath10k: add support for pwrseq sequencing
> > > >      Bluetooth: hci_qca: merge qca_power into qca_serdev
> > > >      Bluetooth: hci_qca: merge init paths
> > > >      Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
> > > >      Bluetooth: hci_qca: futher rework of power on/off handling
> > > >      Bluetooth: hci_qca: add support for pwrseq
> > >
> > > any chance you can try to abandon patching hci_qca. The serdev support in hci_uart is rather hacking into old line discipline code and it is not aging well. It is really becoming a mess.
> >
> > I wanted to stay away from rewriting the BT code. But... New driver
> > would have a bonus point that I don't have to be compatible with old
> > bindings.
>
> It would be preferable if this was a implementation-only change and that
> we kept the existing binding and existing dtb continued to work.

This would require setting up the pwrseq from within the bt driver. I
did not have that in mind. However that'd ease the bt code, since we
won't have to handle the fallback/back-compatibility. Let me think
about it.

-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 00/15] create power sequencing subsystem
  2021-08-20 13:08   ` Dmitry Baryshkov
  2021-08-20 17:02     ` Bjorn Andersson
@ 2021-08-21  6:50     ` Marcel Holtmann
  1 sibling, 0 replies; 28+ messages in thread
From: Marcel Holtmann @ 2021-08-21  6:50 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Andy Gross, Bjorn Andersson, Ulf Hansson, Johan Hedberg,
	Luiz Augusto von Dentz, Kalle Valo, David S. Miller,
	Jakub Kicinski, Stanimir Varbanov, MSM, linux-mmc, open list,
	open list:BLUETOOTH SUBSYSTEM, ath10k, linux-wireless, netdev

Hi Dmitry,

>>> This is an RFC of the proposed power sequencer subsystem. This is a
>>> generification of the MMC pwrseq code. The subsystem tries to abstract
>>> the idea of complex power-up/power-down/reset of the devices.
>>> 
>>> The primary set of devices that promted me to create this patchset is
>>> the Qualcomm BT+WiFi family of chips. They reside on serial+platform
>>> interfaces (older generations) or on serial+PCIe (newer generations).
>>> They require a set of external voltage regulators to be powered on and
>>> (some of them) have separate WiFi and Bluetooth enable GPIOs.
>>> 
>>> This patchset being an RFC tries to demonstrate the approach, design and
>>> usage of the pwrseq subsystem. Following issues are present in the RFC
>>> at this moment but will be fixed later if the overall approach would be
>>> viewed as acceptable:
>>> 
>>> - No documentation
>>> While the code tries to be self-documenting proper documentation
>>> would be required.
>>> 
>>> - Minimal device tree bindings changes
>>> There are no proper updates for the DT bindings (thus neither Rob
>>> Herring nor devicetree are included in the To/Cc lists). The dt
>>> schema changes would be a part of v1.
>>> 
>>> - Lack of proper PCIe integration
>>> At this moment support for PCIe is hacked up to be able to test the
>>> PCIe part of qca6390. Proper PCIe support would require automatically
>>> powering up the devices before the scan basing on the proper device
>>> structure in the device tree.
>>> 
>>> ----------------------------------------------------------------
>>> Dmitry Baryshkov (15):
>>>    power: add power sequencer subsystem
>>>    pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem
>>>    mmc: core: switch to new pwrseq subsystem
>>>    ath10k: add support for pwrseq sequencing
>>>    Bluetooth: hci_qca: merge qca_power into qca_serdev
>>>    Bluetooth: hci_qca: merge init paths
>>>    Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init
>>>    Bluetooth: hci_qca: futher rework of power on/off handling
>>>    Bluetooth: hci_qca: add support for pwrseq
>> 
>> any chance you can try to abandon patching hci_qca. The serdev support in hci_uart is rather hacking into old line discipline code and it is not aging well. It is really becoming a mess.
> 
> I wanted to stay away from rewriting the BT code. But... New driver
> would have a bonus point that I don't have to be compatible with old
> bindings. In fact we can even make it the other way around: let the
> old driver always use regulators and make the new driver support only
> the pwrseq. Then it should be possible to drop the old hci_qca driver
> together with dropping the old bindings.
> 
>> I would say that the Qualcomm serial devices could use a separate standalone serdev driver. A while I send an RFC for a new serdev driver.
>> 
>> https://www.spinics.net/lists/linux-bluetooth/msg74918.html
> 
> Any reason why your driver stayed as an RFC and never made it into the
> kernel? Do you plan to revive your old RFCs on H:4 and H:5?

I was missing enough hardware to test it on and frankly I hoped that someone would pick up this work. The HCI line discipline “hack” needs to be removed soon. It is complicated, cumbersome and has a bunch of issues with locking. Mind you that originated in 2.4.6 kernel and is at its core bit-rotting.

If you manage to put QCA support into a separate btqcauart driver, that would be awesome. The btmtkuart driver is another example where Mediatek got its own serdev based driver.

Regards

Marcel


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2021-08-21  6:50 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-17  0:54 [RFC PATCH 00/15] create power sequencing subsystem Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 01/15] power: add power sequencer subsystem Dmitry Baryshkov
2021-08-19 23:37   ` Bjorn Andersson
2021-08-17  0:54 ` [RFC PATCH 02/15] pwrseq: port MMC's pwrseq drivers to new pwrseq subsystem Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 03/15] mmc: core: switch " Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 04/15] ath10k: add support for pwrseq sequencing Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 05/15] Bluetooth: hci_qca: merge qca_power into qca_serdev Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 06/15] Bluetooth: hci_qca: merge init paths Dmitry Baryshkov
2021-08-17  0:54 ` [RFC PATCH 07/15] Bluetooth: hci_qca: merge qca_power_on with qca_regulators_init Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 08/15] Bluetooth: hci_qca: futher rework of power on/off handling Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 09/15] Bluetooth: hci_qca: add support for pwrseq Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 10/15] pwrseq: add support for QCA BT+WiFi power sequencer Dmitry Baryshkov
2021-08-19 23:18   ` Bjorn Andersson
2021-08-20  8:10     ` Dmitry Baryshkov
2021-08-20 16:35       ` Bjorn Andersson
2021-08-17  0:55 ` [RFC PATCH 11/15] arm64: dts: qcom: sdm845-db845c: switch bt+wifi to qca " Dmitry Baryshkov
2021-08-19 23:40   ` Bjorn Andersson
2021-08-17  0:55 ` [RFC PATCH 12/15] arm64: dts: qcom: qrb5165-rb5: add bluetooth support Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 13/15] arm64: dts: qcom: sdm845-db845c: add second channel support to qca power sequencer Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 14/15] WIP: PCI: qcom: use pwrseq to power up bus devices Dmitry Baryshkov
2021-08-19 23:44   ` Bjorn Andersson
2021-08-20  8:50     ` Dmitry Baryshkov
2021-08-17  0:55 ` [RFC PATCH 15/15] WIP: arm64: dts: qcom: qrb5165-rb5: add bus-pwrseq property to pcie0 Dmitry Baryshkov
2021-08-19 15:23 ` [RFC PATCH 00/15] create power sequencing subsystem Marcel Holtmann
2021-08-20 13:08   ` Dmitry Baryshkov
2021-08-20 17:02     ` Bjorn Andersson
2021-08-20 18:06       ` Dmitry Baryshkov
2021-08-21  6:50     ` Marcel Holtmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).