linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] soc: qcom: Introduce PMIC GLINK
@ 2022-08-18  3:15 Bjorn Andersson
  2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
                   ` (5 more replies)
  0 siblings, 6 replies; 18+ messages in thread
From: Bjorn Andersson @ 2022-08-18  3:15 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Sebastian Reichel
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm

This implements the base PMIC GLINK driver, a power_supply driver and a driver
for the USB Type-C altmode protocol. This has been tested and shown to provide
battery information, USB Type-C switch and mux requests and DisplayPort
notifications on SC8180X, SC8280XP and SM8350.

Bjorn Andersson (4):
  dt-bindings: soc: qcom: Introduce PMIC GLINK binding
  soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  soc: qcom: pmic_glink: Introduce altmode support
  power: supply: Introduce Qualcomm PMIC GLINK power supply

 .../bindings/soc/qcom/qcom,pmic-glink.yaml    |   98 ++
 drivers/power/supply/Kconfig                  |    9 +
 drivers/power/supply/Makefile                 |    1 +
 drivers/power/supply/qcom_battmgr.c           | 1422 +++++++++++++++++
 drivers/soc/qcom/Kconfig                      |   14 +
 drivers/soc/qcom/Makefile                     |    2 +
 drivers/soc/qcom/pmic_glink.c                 |  336 ++++
 drivers/soc/qcom/pmic_glink_altmode.c         |  477 ++++++
 include/linux/soc/qcom/pmic_glink.h           |   32 +
 9 files changed, 2391 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
 create mode 100644 drivers/power/supply/qcom_battmgr.c
 create mode 100644 drivers/soc/qcom/pmic_glink.c
 create mode 100644 drivers/soc/qcom/pmic_glink_altmode.c
 create mode 100644 include/linux/soc/qcom/pmic_glink.h

-- 
2.35.1


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

* [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
@ 2022-08-18  3:15 ` Bjorn Andersson
  2022-08-18  7:55   ` Krzysztof Kozlowski
  2022-08-22 12:39   ` Rob Herring
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 18+ messages in thread
From: Bjorn Andersson @ 2022-08-18  3:15 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Sebastian Reichel
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm

The PMIC GLINK service, running on a coprocessor on some modern Qualcomm
platforms and implement USB Type-C handling and battery management.
This binding describes the component in the OS used to communicate with
the firmware and connect it's resources to those described in the
Devicetree, particularly the USB Type-C controllers relationship with
USB and DisplayPort components.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 .../bindings/soc/qcom/qcom,pmic-glink.yaml    | 98 +++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml

diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
new file mode 100644
index 000000000000..3261f9d27a47
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/qcom/qcom,pmic-glink.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm PMIC GLINK firmware interface for battery management, USB
+  Type-C and other things.
+
+maintainers:
+  - Bjorn Andersson <bjorn.andersson@linaro.org>
+
+description:
+  The PMIC GLINK service, running on a coprocessor on some modern Qualcomm
+  platforms and implement USB Type-C handling and battery management. This
+  binding describes the component in the OS used to communicate with the
+  firmware and connect it's resources to those described in the Devicetree,
+  particularly the USB Type-C controllers relationship with USB and DisplayPort
+  components.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - qcom,sc8180x-pmic-glink
+          - qcom,sc8280xp-pmic-glink
+          - qcom,sm8350-pmic-glink
+      - const: qcom,pmic-glink
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+patternProperties:
+  '^connector@\d$':
+    $ref: /schemas/connector/usb-connector.yaml#
+    unevaluatedProperties: false
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |+
+    pmic-glink {
+        compatible = "qcom,sc8280xp-pmic-glink", "qcom,pmic-glink";
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        connector@0 {
+            compatible = "usb-c-connector";
+            reg = <0>;
+            power-role = "dual";
+            data-role = "dual";
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    endpoint {
+                        remote-endpoint = <&usb_role>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    endpoint@0 {
+                        reg = <0>;
+                        remote-endpoint = <&qmp_out>;
+                    };
+
+                    endpoint@1 {
+                        reg = <1>;
+                        remote-endpoint = <&displayport_hpd>;
+                    };
+                };
+
+                port@2 {
+                    reg = <2>;
+                    endpoint {
+                        remote-endpoint = <&sbu_mux>;
+                    };
+                };
+            };
+        };
+    };
+...
+
-- 
2.35.1


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

* [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
  2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
@ 2022-08-18  3:15 ` Bjorn Andersson
  2022-08-18  8:05   ` Krzysztof Kozlowski
                     ` (4 more replies)
  2022-08-18  3:15 ` [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support Bjorn Andersson
                   ` (3 subsequent siblings)
  5 siblings, 5 replies; 18+ messages in thread
From: Bjorn Andersson @ 2022-08-18  3:15 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio
  Cc: Rob Herring, Krzysztof Kozlowski, Sebastian Reichel,
	linux-arm-msm, devicetree, linux-kernel, linux-pm

The PMIC GLINK service runs on one of the co-processors of some modern
Qualcomm platforms and implements USB-C and battery managements. It uses
a message based protocol over GLINK for communication with the OS, hence
the name.

The driver implemented provides the rpmsg device for communication and
uses auxilirary bus to spawn off individual devices in respsective
subsystem. The auxilirary devices are spawned off from a
platform_device, so that the drm_bridge is available early, to allow the
DisplayPort driver to probe even before the remoteproc has spun up.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 drivers/soc/qcom/Kconfig            |  14 ++
 drivers/soc/qcom/Makefile           |   1 +
 drivers/soc/qcom/pmic_glink.c       | 336 ++++++++++++++++++++++++++++
 include/linux/soc/qcom/pmic_glink.h |  32 +++
 4 files changed, 383 insertions(+)
 create mode 100644 drivers/soc/qcom/pmic_glink.c
 create mode 100644 include/linux/soc/qcom/pmic_glink.h

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e0d7a5459562..2289f5e0d5ad 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS
 	tristate
 	select QCOM_QMI_HELPERS
 
+config QCOM_PMIC_GLINK
+	tristate "Qualcomm PMIC GLINK driver"
+	depends on RPMSG
+	depends on TYPEC
+	depends on DRM
+	select QCOM_PDR_HELPERS
+	help
+	  The Qualcomm PMIC GLINK driver provides access, over GLINK, to the
+	  USB and battery firmware running on one of the coprocessors in
+	  several modern Qualcomm platforms.
+
+	  Say yes here to support USB-C and battery status on modern Qualcomm
+	  platforms.
+
 config QCOM_QMI_HELPERS
 	tristate
 	depends on NET
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index d66604aff2b0..fbbd1231e554 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
 obj-$(CONFIG_QCOM_OCMEM)	+= ocmem.o
 obj-$(CONFIG_QCOM_PDR_HELPERS)	+= pdr_interface.o
+obj-$(CONFIG_QCOM_PMIC_GLINK)	+= pmic_glink.o
 obj-$(CONFIG_QCOM_QMI_HELPERS)	+= qmi_helpers.o
 qmi_helpers-y	+= qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)	+= rmtfs_mem.o
diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c
new file mode 100644
index 000000000000..d42127521eca
--- /dev/null
+++ b/drivers/soc/qcom/pmic_glink.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Linaro Ltd
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/pdr.h>
+#include <linux/soc/qcom/pmic_glink.h>
+
+struct pmic_glink {
+	struct device *dev;
+	struct pdr_handle *pdr;
+
+	struct rpmsg_endpoint *ept;
+
+	struct auxiliary_device altmode_aux;
+	struct auxiliary_device ps_aux;
+	struct auxiliary_device ucsi_aux;
+
+	/* serializing client_state and pdr_state updates */
+	struct mutex state_lock;
+	unsigned int client_state;
+	unsigned int pdr_state;
+
+	/* serializing clients list updates */
+	struct mutex client_lock;
+	struct list_head clients;
+};
+
+static struct pmic_glink *__pmic_glink;
+static DEFINE_MUTEX(__pmic_glink_lock);
+
+struct pmic_glink_client {
+	struct list_head node;
+
+	struct pmic_glink *pmic;
+	unsigned int id;
+
+	void (*cb)(const void *data, size_t len, void *priv);
+	void (*pdr_notify)(void *priv, int state);
+	void *priv;
+};
+
+static void _devm_pmic_glink_release_client(struct device *dev, void *res)
+{
+	struct pmic_glink_client *client = *(struct pmic_glink_client **)res;
+	struct pmic_glink *pg = client->pmic;
+
+	mutex_lock(&pg->client_lock);
+	list_del(&client->node);
+	mutex_unlock(&pg->client_lock);
+}
+
+struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
+							  unsigned int id,
+							  void (*cb)(const void *, size_t, void *),
+							  void (*pdr)(void *, int),
+							  void *priv)
+{
+	struct pmic_glink_client *client;
+	struct pmic_glink *pg = dev_get_drvdata(dev->parent);
+
+	client = devres_alloc(_devm_pmic_glink_release_client, sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return ERR_PTR(-ENOMEM);
+
+	client->pmic = pg;
+	client->id = id;
+	client->cb = cb;
+	client->pdr_notify = pdr;
+	client->priv = priv;
+
+	mutex_lock(&pg->client_lock);
+	list_add(&client->node, &pg->clients);
+	mutex_unlock(&pg->client_lock);
+
+	devres_add(dev, client);
+
+	return client;
+}
+EXPORT_SYMBOL_GPL(devm_pmic_glink_register_client);
+
+int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len)
+{
+	struct pmic_glink *pg = client->pmic;
+
+	return rpmsg_send(pg->ept, data, len);
+}
+EXPORT_SYMBOL_GPL(pmic_glink_send);
+
+static int pmic_glink_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
+				     int len, void *priv, u32 addr)
+{
+	struct pmic_glink_client *client;
+	struct pmic_glink_hdr *hdr;
+	struct pmic_glink *pg = dev_get_drvdata(&rpdev->dev);
+
+	if (len < sizeof(*hdr)) {
+		dev_warn(pg->dev, "ignoring truncated message\n");
+		return 0;
+	}
+
+	hdr = data;
+
+	list_for_each_entry(client, &pg->clients, node) {
+		if (client->id == le32_to_cpu(hdr->owner))
+			client->cb(data, len, client->priv);
+	}
+
+	return 0;
+}
+
+static void pmic_glink_aux_release(struct device *dev) {}
+
+static int pmic_glink_add_aux_device(struct pmic_glink *pg,
+				     struct auxiliary_device *aux,
+				     const char *name)
+{
+	struct device *parent = pg->dev;
+	int ret;
+
+	aux->name = name;
+	aux->dev.parent = parent;
+	aux->dev.release = pmic_glink_aux_release;
+	device_set_of_node_from_dev(&aux->dev, parent);
+	ret = auxiliary_device_init(aux);
+	if (ret)
+		return ret;
+
+	ret = auxiliary_device_add(aux);
+	if (ret)
+		auxiliary_device_uninit(aux);
+
+	return ret;
+}
+
+static void pmic_glink_del_aux_device(struct pmic_glink *pg,
+				      struct auxiliary_device *aux)
+{
+	auxiliary_device_delete(aux);
+	auxiliary_device_uninit(aux);
+}
+
+static void pmic_glink_state_notify_clients(struct pmic_glink *pg)
+{
+	struct pmic_glink_client *client;
+	unsigned int new_state = pg->client_state;
+
+	if (pg->client_state != SERVREG_SERVICE_STATE_UP) {
+		if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept)
+			new_state = SERVREG_SERVICE_STATE_UP;
+	} else {
+		if (pg->pdr_state == SERVREG_SERVICE_STATE_UP && pg->ept)
+			new_state = SERVREG_SERVICE_STATE_DOWN;
+	}
+
+	if (new_state != pg->client_state) {
+		list_for_each_entry(client, &pg->clients, node)
+			client->pdr_notify(client->priv, new_state);
+		pg->client_state = new_state;
+	}
+}
+
+static void pmic_glink_pdr_callback(int state, char *svc_path, void *priv)
+{
+	struct pmic_glink *pg = priv;
+
+	mutex_lock(&pg->state_lock);
+	pg->pdr_state = state;
+
+	pmic_glink_state_notify_clients(pg);
+	mutex_unlock(&pg->state_lock);
+}
+
+static int pmic_glink_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+	struct pmic_glink *pg = __pmic_glink;
+	int ret = 0;
+
+	mutex_lock(&__pmic_glink_lock);
+	if (!pg) {
+		ret = dev_err_probe(&rpdev->dev, -ENODEV, "no pmic_glink device to attach to\n");
+		goto out_unlock;
+	}
+
+	dev_set_drvdata(&rpdev->dev, pg);
+
+	mutex_lock(&pg->state_lock);
+	pg->ept = rpdev->ept;
+	pmic_glink_state_notify_clients(pg);
+	mutex_unlock(&pg->state_lock);
+
+out_unlock:
+	mutex_unlock(&__pmic_glink_lock);
+	return ret;
+}
+
+static void pmic_glink_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+	struct pmic_glink *pg;
+
+	mutex_lock(&__pmic_glink_lock);
+	pg = __pmic_glink;
+	if (!pg)
+		goto out_unlock;
+
+	mutex_lock(&pg->state_lock);
+	pg->ept = NULL;
+	pmic_glink_state_notify_clients(pg);
+	mutex_unlock(&pg->state_lock);
+out_unlock:
+	mutex_unlock(&__pmic_glink_lock);
+}
+
+static const struct rpmsg_device_id pmic_glink_rpmsg_id_match[] = {
+	{ "PMIC_RTR_ADSP_APPS" },
+	{}
+};
+
+static struct rpmsg_driver pmic_glink_rpmsg_driver = {
+	.probe = pmic_glink_rpmsg_probe,
+	.remove = pmic_glink_rpmsg_remove,
+	.callback = pmic_glink_rpmsg_callback,
+	.id_table = pmic_glink_rpmsg_id_match,
+	.drv  = {
+		.name  = "qcom_pmic_glink_rpmsg",
+	},
+};
+
+static int pmic_glink_probe(struct platform_device *pdev)
+{
+	struct pdr_service *service;
+	struct pmic_glink *pg;
+	int ret;
+
+	pg = devm_kzalloc(&pdev->dev, sizeof(*pg), GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, pg);
+
+	pg->dev = &pdev->dev;
+
+	INIT_LIST_HEAD(&pg->clients);
+	mutex_init(&pg->client_lock);
+	mutex_init(&pg->state_lock);
+
+	ret = pmic_glink_add_aux_device(pg, &pg->altmode_aux, "altmode");
+	if (ret)
+		return ret;
+	ret = pmic_glink_add_aux_device(pg, &pg->ps_aux, "power-supply");
+	if (ret)
+		goto out_release_altmode_aux;
+
+	pg->pdr = pdr_handle_alloc(pmic_glink_pdr_callback, pg);
+	if (IS_ERR(pg->pdr)) {
+		ret = dev_err_probe(&pdev->dev, PTR_ERR(pg->pdr), "failed to initialize pdr\n");
+		goto out_release_aux_devices;
+	}
+
+	service = pdr_add_lookup(pg->pdr, "tms/servreg", "msm/adsp/charger_pd");
+	if (IS_ERR(service)) {
+		ret = dev_err_probe(&pdev->dev, PTR_ERR(service),
+				    "failed adding pdr lookup for charger_pd\n");
+		goto out_release_pdr_handle;
+	}
+
+	mutex_lock(&__pmic_glink_lock);
+	__pmic_glink = pg;
+	mutex_unlock(&__pmic_glink_lock);
+
+	return 0;
+
+out_release_pdr_handle:
+	pdr_handle_release(pg->pdr);
+out_release_aux_devices:
+	pmic_glink_del_aux_device(pg, &pg->ps_aux);
+out_release_altmode_aux:
+	pmic_glink_del_aux_device(pg, &pg->altmode_aux);
+
+	return ret;
+}
+
+static int pmic_glink_remove(struct platform_device *pdev)
+{
+	struct pmic_glink *pg = dev_get_drvdata(&pdev->dev);
+
+	pdr_handle_release(pg->pdr);
+
+	pmic_glink_del_aux_device(pg, &pg->ps_aux);
+	pmic_glink_del_aux_device(pg, &pg->altmode_aux);
+
+	mutex_lock(&__pmic_glink_lock);
+	__pmic_glink = NULL;
+	mutex_unlock(&__pmic_glink_lock);
+
+	return 0;
+}
+
+static const struct of_device_id pmic_glink_of_match[] = {
+	{ .compatible = "qcom,pmic-glink", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pmic_glink_of_match);
+
+static struct platform_driver pmic_glink_driver = {
+	.probe = pmic_glink_probe,
+	.remove = pmic_glink_remove,
+	.driver = {
+		.name = "qcom_pmic_glink",
+		.of_match_table = pmic_glink_of_match,
+	},
+};
+
+static int pmic_glink_init(void)
+{
+	platform_driver_register(&pmic_glink_driver);
+	register_rpmsg_driver(&pmic_glink_rpmsg_driver);
+
+	return 0;
+};
+module_init(pmic_glink_init);
+
+static void pmic_glink_exit(void)
+{
+	platform_driver_unregister(&pmic_glink_driver);
+	unregister_rpmsg_driver(&pmic_glink_rpmsg_driver);
+};
+module_exit(pmic_glink_exit);
+
+MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h
new file mode 100644
index 000000000000..40470f8dfc1e
--- /dev/null
+++ b/include/linux/soc/qcom/pmic_glink.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, Linaro Ltd
+ */
+#ifndef __PMIC_GLINK_H__
+#define __PMIC_GLINK_H__
+
+struct pmic_glink;
+struct pmic_glink_client;
+
+#define PMIC_GLINK_OWNER_BATTMGR	32778
+#define PMIC_GLINK_OWNER_USBC		32779
+#define PMIC_GLINK_OWNER_USBC_PAN	32780
+
+#define PMIC_GLINK_REQ_RESP		1
+#define PMIC_GLINK_NOTIFY		2
+
+struct pmic_glink_hdr {
+	__le32 owner;
+	__le32 type;
+	__le32 opcode;
+};
+
+int pmic_glink_send(struct pmic_glink_client *client, void *data, size_t len);
+
+struct pmic_glink_client *devm_pmic_glink_register_client(struct device *dev,
+							  unsigned int id,
+							  void (*cb)(const void *, size_t, void *),
+							  void (*pdr)(void *, int),
+							  void *priv);
+
+#endif
-- 
2.35.1


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

* [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
  2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
@ 2022-08-18  3:15 ` Bjorn Andersson
  2022-10-25  8:24   ` Johan Hovold
  2022-10-25  8:32   ` Johan Hovold
  2022-08-18  3:15 ` [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply Bjorn Andersson
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 18+ messages in thread
From: Bjorn Andersson @ 2022-08-18  3:15 UTC (permalink / raw)
  To: Andy Gross, Bjorn Andersson, Konrad Dybcio
  Cc: Rob Herring, Krzysztof Kozlowski, Sebastian Reichel,
	linux-arm-msm, devicetree, linux-kernel, linux-pm

With the PMIC GLINK service, the host OS subscribes to USB-C altmode
messages, which are sent by the firmware to notify the host OS about
state updates and HPD interrupts.

The pmic_glink_altmode driver registers for these notifications and
propagates the notifications as typec_mux, typec_switch and DRM OOB
notifications as necessary to implement DisplayPort altmode support.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 drivers/soc/qcom/Makefile             |   1 +
 drivers/soc/qcom/pmic_glink_altmode.c | 477 ++++++++++++++++++++++++++
 2 files changed, 478 insertions(+)
 create mode 100644 drivers/soc/qcom/pmic_glink_altmode.c

diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fbbd1231e554..8d62a17863c1 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
 obj-$(CONFIG_QCOM_OCMEM)	+= ocmem.o
 obj-$(CONFIG_QCOM_PDR_HELPERS)	+= pdr_interface.o
 obj-$(CONFIG_QCOM_PMIC_GLINK)	+= pmic_glink.o
+obj-$(CONFIG_QCOM_PMIC_GLINK)	+= pmic_glink_altmode.o
 obj-$(CONFIG_QCOM_QMI_HELPERS)	+= qmi_helpers.o
 qmi_helpers-y	+= qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)	+= rmtfs_mem.o
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
new file mode 100644
index 000000000000..8d2d563cb756
--- /dev/null
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Linaro Ltd
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/soc/qcom/pdr.h>
+#include <drm/drm_bridge.h>
+
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+
+#include <linux/soc/qcom/pmic_glink.h>
+
+#define PMIC_GLINK_MAX_PORTS	2
+
+#define USBC_SC8180X_NOTIFY_IND	0x13
+#define USBC_CMD_WRITE_REQ      0x15
+#define USBC_NOTIFY_IND		0x16
+
+#define ALTMODE_PAN_EN		0x10
+#define ALTMODE_PAN_ACK		0x11
+
+struct usbc_write_req {
+	struct pmic_glink_hdr   hdr;
+	__le32 cmd;
+	__le32 arg;
+	__le32 reserved;
+};
+
+#define NOTIFY_PAYLOAD_SIZE 16
+struct usbc_notify {
+	struct pmic_glink_hdr hdr;
+	char payload[NOTIFY_PAYLOAD_SIZE];
+	u32 reserved;
+};
+
+struct usbc_sc8180x_notify {
+	struct pmic_glink_hdr hdr;
+	__le32 notification;
+	__le32 reserved[2];
+};
+
+enum pmic_glink_altmode_pin_assignment {
+	DPAM_HPD_OUT,
+	DPAM_HPD_A,
+	DPAM_HPD_B,
+	DPAM_HPD_C,
+	DPAM_HPD_D,
+	DPAM_HPD_E,
+	DPAM_HPD_F,
+};
+
+struct pmic_glink_altmode;
+
+#define work_to_altmode_port(w) container_of((w), struct pmic_glink_altmode_port, work)
+
+struct pmic_glink_altmode_port {
+	struct pmic_glink_altmode *altmode;
+	unsigned int index;
+
+	struct typec_switch *typec_switch;
+	struct typec_mux *typec_mux;
+	struct typec_mux_state state;
+	struct typec_altmode dp_alt;
+
+	struct work_struct work;
+
+	struct drm_bridge bridge;
+
+	enum typec_orientation orientation;
+	u16 svid;
+	u8 dp_data;
+	u8 mode;
+	u8 hpd_state;
+	u8 hpd_irq;
+};
+
+#define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work)
+
+struct pmic_glink_altmode {
+	struct device *dev;
+
+	unsigned int owner_id;
+
+	/* To synchronize WRITE_REQ acks */
+	struct mutex lock;
+
+	struct completion pan_ack;
+	struct pmic_glink_client *client;
+
+	struct work_struct enable_work;
+
+	struct pmic_glink_altmode_port ports[PMIC_GLINK_MAX_PORTS];
+};
+
+static int pmic_glink_altmode_request(struct pmic_glink_altmode *altmode, u32 cmd, u32 arg)
+{
+	struct usbc_write_req req = {};
+	unsigned long left;
+	int ret;
+
+	/*
+	 * The USBC_CMD_WRITE_REQ ack doesn't identify the request, so wait for
+	 * one ack at a time.
+	 */
+	mutex_lock(&altmode->lock);
+
+	req.hdr.owner = cpu_to_le32(altmode->owner_id);
+	req.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP);
+	req.hdr.opcode = cpu_to_le32(USBC_CMD_WRITE_REQ);
+	req.cmd = cpu_to_le32(cmd);
+	req.arg = cpu_to_le32(arg);
+
+	ret = pmic_glink_send(altmode->client, &req, sizeof(req));
+	if (ret) {
+		dev_err(altmode->dev, "failed to send altmode request: %#x (%d)\n", cmd, ret);
+		goto out_unlock;
+	}
+
+	left = wait_for_completion_timeout(&altmode->pan_ack, 5 * HZ);
+	if (!left) {
+		dev_err(altmode->dev, "timeout waiting for altmode request ack for: %#x\n", cmd);
+		ret = -ETIMEDOUT;
+	}
+
+out_unlock:
+	mutex_unlock(&altmode->lock);
+	return ret;
+}
+
+static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
+					 struct pmic_glink_altmode_port *port,
+					 u8 mode, bool hpd_state,
+					 bool hpd_irq)
+{
+	struct typec_displayport_data dp_data = {};
+	int ret;
+
+	dp_data.status = DP_STATUS_ENABLED;
+	if (hpd_state)
+		dp_data.status |= DP_STATUS_HPD_STATE;
+	if (hpd_irq)
+		dp_data.status |= DP_STATUS_IRQ_HPD;
+	dp_data.conf = DP_CONF_SET_PIN_ASSIGN(mode);
+
+	port->state.alt = &port->dp_alt;
+	port->state.data = &dp_data;
+	port->state.mode = TYPEC_MODAL_STATE(mode);
+
+	ret = typec_mux_set(port->typec_mux, &port->state);
+	if (ret)
+		dev_err(altmode->dev, "failed to switch mux to DP\n");
+}
+
+static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
+					  struct pmic_glink_altmode_port *port)
+{
+	int ret;
+
+	port->state.alt = NULL;
+	port->state.data = NULL;
+	port->state.mode = TYPEC_STATE_USB;
+
+	ret = typec_mux_set(port->typec_mux, &port->state);
+	if (ret)
+		dev_err(altmode->dev, "failed to switch mux to USB\n");
+}
+
+static void pmic_glink_altmode_worker(struct work_struct *work)
+{
+	struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work);
+	struct pmic_glink_altmode *altmode = alt_port->altmode;
+
+	typec_switch_set(alt_port->typec_switch, alt_port->orientation);
+
+	if (alt_port->svid == USB_TYPEC_DP_SID)
+		pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode,
+					     alt_port->hpd_state, alt_port->hpd_irq);
+	else
+		pmic_glink_altmode_enable_usb(altmode, alt_port);
+
+	if (alt_port->hpd_state)
+		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected);
+	else
+		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected);
+
+	pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index);
+};
+
+static enum typec_orientation pmic_glink_altmode_orientation(unsigned int orientation)
+{
+	if (orientation == 0)
+		return TYPEC_ORIENTATION_NORMAL;
+	else if (orientation == 1)
+		return TYPEC_ORIENTATION_REVERSE;
+	else
+		return TYPEC_ORIENTATION_NONE;
+}
+
+#define SC8180X_PORT_MASK		0x000000ff
+#define SC8180X_ORIENTATION_MASK	0x0000ff00
+#define SC8180X_MUX_MASK		0x00ff0000
+#define SC8180X_MODE_MASK		0x3f000000
+#define SC8180X_HPD_STATE_MASK		0x40000000
+#define SC8180X_HPD_IRQ_MASK		0x80000000
+
+static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmode,
+					       const void *data, size_t len)
+{
+	struct pmic_glink_altmode_port *alt_port;
+	const struct usbc_sc8180x_notify *msg;
+	u32 notification;
+	u8 orientation;
+	u8 hpd_state;
+	u8 hpd_irq;
+	u16 svid;
+	u8 port;
+	u8 mode;
+	u8 mux;
+
+	if (len != sizeof(*msg)) {
+		dev_warn(altmode->dev, "invalid length of USBC_NOTIFY indication: %zd\n", len);
+		return;
+	}
+
+	msg = data;
+	notification = le32_to_cpu(msg->notification);
+	port = FIELD_GET(SC8180X_PORT_MASK, notification);
+	orientation = FIELD_GET(SC8180X_ORIENTATION_MASK, notification);
+	mux = FIELD_GET(SC8180X_MUX_MASK, notification);
+	mode = FIELD_GET(SC8180X_MODE_MASK, notification);
+	hpd_state = FIELD_GET(SC8180X_HPD_STATE_MASK, notification);
+	hpd_irq = FIELD_GET(SC8180X_HPD_IRQ_MASK, notification);
+
+	svid = mux == 2 ? USB_TYPEC_DP_SID : 0;
+
+	if (!altmode->ports[port].altmode) {
+		dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
+		return;
+	}
+
+	alt_port = &altmode->ports[port];
+	alt_port->orientation = pmic_glink_altmode_orientation(orientation);
+	alt_port->svid = mux == 2 ? USB_TYPEC_DP_SID : 0;
+	alt_port->mode = mode;
+	alt_port->hpd_state = hpd_state;
+	alt_port->hpd_irq = hpd_irq;
+	schedule_work(&alt_port->work);
+}
+
+#define SC8280XP_DPAM_MASK	0x3f
+#define SC8280XP_HPD_STATE_MASK BIT(6)
+#define SC8280XP_HPD_IRQ_MASK	BIT(7)
+
+static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmode,
+					       u16 svid, const void *data, size_t len)
+{
+	struct pmic_glink_altmode_port *alt_port;
+	const struct usbc_notify *notify;
+	u8 orientation;
+	u8 hpd_state;
+	u8 hpd_irq;
+	u8 mode;
+	u8 port;
+
+	if (len != sizeof(*notify)) {
+		dev_warn(altmode->dev, "invalid length USBC_NOTIFY_IND: %zd\n",
+			 len);
+		return;
+	}
+
+	notify = data;
+
+	port = notify->payload[0];
+	orientation = notify->payload[1];
+	mode = FIELD_GET(SC8280XP_DPAM_MASK, notify->payload[8]) - DPAM_HPD_A;
+	hpd_state = FIELD_GET(SC8280XP_HPD_STATE_MASK, notify->payload[8]);
+	hpd_irq = FIELD_GET(SC8280XP_HPD_IRQ_MASK, notify->payload[8]);
+
+	if (!altmode->ports[port].altmode) {
+		dev_dbg(altmode->dev, "notification on undefined port %d\n", port);
+		return;
+	}
+
+	alt_port = &altmode->ports[port];
+	alt_port->orientation = pmic_glink_altmode_orientation(orientation);
+	alt_port->svid = svid;
+	alt_port->mode = mode;
+	alt_port->hpd_state = hpd_state;
+	alt_port->hpd_irq = hpd_irq;
+	schedule_work(&alt_port->work);
+}
+
+static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv)
+{
+	struct pmic_glink_altmode *altmode = priv;
+	const struct pmic_glink_hdr *hdr = data;
+	u16 opcode;
+	u16 svid;
+
+	opcode = le32_to_cpu(hdr->opcode) & 0xff;
+	svid = le32_to_cpu(hdr->opcode) >> 16;
+
+	switch (opcode) {
+	case USBC_CMD_WRITE_REQ:
+		complete(&altmode->pan_ack);
+		break;
+	case USBC_NOTIFY_IND:
+		pmic_glink_altmode_sc8280xp_notify(altmode, svid, data, len);
+		break;
+	case USBC_SC8180X_NOTIFY_IND:
+		pmic_glink_altmode_sc8180xp_notify(altmode, data, len);
+		break;
+	}
+}
+
+static int pmic_glink_altmode_attach(struct drm_bridge *bridge,
+				     enum drm_bridge_attach_flags flags)
+{
+	return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static const struct drm_bridge_funcs pmic_glink_altmode_bridge_funcs = {
+	.attach = pmic_glink_altmode_attach,
+};
+
+static void pmic_glink_altmode_put_mux(void *data)
+{
+	typec_mux_put(data);
+}
+
+static void pmic_glink_altmode_put_switch(void *data)
+{
+	typec_switch_put(data);
+}
+
+static void pmic_glink_altmode_enable_worker(struct work_struct *work)
+{
+	struct pmic_glink_altmode *altmode = work_to_altmode(work);
+	int ret;
+
+	ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0);
+	if (ret)
+		dev_err(altmode->dev, "failed to request altmode notifications\n");
+}
+
+static void pmic_glink_altmode_pdr_notify(void *priv, int state)
+{
+	struct pmic_glink_altmode *altmode = priv;
+
+	if (state == SERVREG_SERVICE_STATE_UP)
+		schedule_work(&altmode->enable_work);
+}
+
+static const struct of_device_id pmic_glink_altmode_of_quirks[] = {
+	{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)PMIC_GLINK_OWNER_USBC },
+	{}
+};
+
+static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
+				    const struct auxiliary_device_id *id)
+{
+	struct pmic_glink_altmode_port *alt_port;
+	struct pmic_glink_altmode *altmode;
+	struct typec_altmode_desc mux_desc = {};
+	const struct of_device_id *match;
+	struct fwnode_handle *fwnode;
+	struct device *dev = &adev->dev;
+	u32 port;
+	int ret;
+
+	altmode = devm_kzalloc(dev, sizeof(*altmode), GFP_KERNEL);
+	if (!altmode)
+		return -ENOMEM;
+
+	altmode->dev = dev;
+
+	match = of_match_device(pmic_glink_altmode_of_quirks, dev->parent);
+	if (match)
+		altmode->owner_id = (unsigned long)match->data;
+	else
+		altmode->owner_id = PMIC_GLINK_OWNER_USBC_PAN;
+
+	INIT_WORK(&altmode->enable_work, pmic_glink_altmode_enable_worker);
+	init_completion(&altmode->pan_ack);
+	mutex_init(&altmode->lock);
+
+	device_for_each_child_node(dev, fwnode) {
+		ret = fwnode_property_read_u32(fwnode, "reg", &port);
+		if (ret < 0) {
+			dev_err(dev, "missing reg property of %pOFn\n", fwnode);
+			return ret;
+		}
+
+		if (port >= ARRAY_SIZE(altmode->ports)) {
+			dev_warn(dev, "invalid connector number, ignoring\n");
+			continue;
+		}
+
+		if (altmode->ports[port].altmode) {
+			dev_err(dev, "multiple connector definition for port %u\n", port);
+			return -EINVAL;
+		}
+
+		alt_port = &altmode->ports[port];
+		alt_port->altmode = altmode;
+		alt_port->index = port;
+		INIT_WORK(&alt_port->work, pmic_glink_altmode_worker);
+
+		alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs;
+		alt_port->bridge.of_node = to_of_node(fwnode);
+		alt_port->bridge.ops = DRM_BRIDGE_OP_HPD;
+		alt_port->bridge.type = DRM_MODE_CONNECTOR_USB;
+
+		ret = devm_drm_bridge_add(dev, &alt_port->bridge);
+		if (ret)
+			return ret;
+
+		alt_port->dp_alt.svid = USB_TYPEC_DP_SID;
+		alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
+		alt_port->dp_alt.active = 1;
+
+		mux_desc.svid = USB_TYPEC_DP_SID;
+		mux_desc.mode = USB_TYPEC_DP_MODE;
+		alt_port->typec_mux = fwnode_typec_mux_get(fwnode, &mux_desc);
+		if (IS_ERR(alt_port->typec_mux))
+			return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux),
+					     "failed to acquire mode-switch for port: %d\n",
+					     port);
+
+		ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux,
+					       alt_port->typec_mux);
+		if (ret)
+			return ret;
+
+		alt_port->typec_switch = fwnode_typec_switch_get(fwnode);
+		if (IS_ERR(alt_port->typec_switch))
+			return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch),
+					     "failed to acquire orientation-switch for port: %d\n",
+					     port);
+
+		ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch,
+					       alt_port->typec_switch);
+		if (ret)
+			return ret;
+	}
+
+	altmode->client = devm_pmic_glink_register_client(dev,
+							  altmode->owner_id,
+							  pmic_glink_altmode_callback,
+							  pmic_glink_altmode_pdr_notify,
+							  altmode);
+	return PTR_ERR_OR_ZERO(altmode->client);
+}
+
+static const struct auxiliary_device_id pmic_glink_altmode_id_table[] = {
+	{ .name = "pmic_glink.altmode", },
+	{},
+};
+MODULE_DEVICE_TABLE(auxiliary, pmic_glink_altmode_id_table);
+
+static struct auxiliary_driver pmic_glink_altmode_driver = {
+	.name = "pmic_glink_altmode",
+	.probe = pmic_glink_altmode_probe,
+	.id_table = pmic_glink_altmode_id_table,
+};
+
+module_auxiliary_driver(pmic_glink_altmode_driver);
+
+MODULE_DESCRIPTION("Qualcomm PMIC GLINK Altmode driver");
+MODULE_LICENSE("GPL");
-- 
2.35.1


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

* [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
                   ` (2 preceding siblings ...)
  2022-08-18  3:15 ` [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support Bjorn Andersson
@ 2022-08-18  3:15 ` Bjorn Andersson
  2022-08-30 15:25   ` Johan Hovold
  2022-09-20  2:49   ` Steev Klimaszewski
  2022-08-18 19:25 ` [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Steev Klimaszewski
  2023-02-06 22:30 ` Bjorn Andersson
  5 siblings, 2 replies; 18+ messages in thread
From: Bjorn Andersson @ 2022-08-18  3:15 UTC (permalink / raw)
  To: Konrad Dybcio, Sebastian Reichel
  Cc: Andy Gross, Rob Herring, Krzysztof Kozlowski, linux-arm-msm,
	devicetree, linux-kernel, linux-pm

The PMIC GLINK service, running on a coprocessor of modern Qualcomm
platforms, deals with battery charging and fuel gauging, as well as
reporting status of AC and wireless power supplies.

As this is just one of the functionalities provided by the PMIC GLINK
service, this power supply driver is implemented as an auxilirary bus
driver, spawned by the main "pmic glink" driver when the PMIC GLINK
service is detected.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 drivers/power/supply/Kconfig        |    9 +
 drivers/power/supply/Makefile       |    1 +
 drivers/power/supply/qcom_battmgr.c | 1422 +++++++++++++++++++++++++++
 3 files changed, 1432 insertions(+)
 create mode 100644 drivers/power/supply/qcom_battmgr.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 1aa8323ad9f6..490386ae729f 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -183,6 +183,15 @@ config BATTERY_PMU
 	  Say Y here to expose battery information on Apple machines
 	  through the generic battery class.
 
+config BATTERY_QCOM_BATTMGR
+	tristate "Qualcomm PMIC GLINK battery manager support"
+	depends on QCOM_PMIC_GLINK
+	select AUXILIARY_BUS
+	help
+	  Say Y here to enable the Qualcomm PMIC GLINK power supply driver,
+	  which is used on modern Qualcomm platforms to provide battery and
+	  power supply information.
+
 config BATTERY_OLPC
 	tristate "One Laptop Per Child battery"
 	depends on OLPC_EC
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 7f02f36aea55..f93d777487f6 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_GAUGE_LTC2941)	+= ltc2941-battery-gauge.o
 obj-$(CONFIG_BATTERY_GOLDFISH)	+= goldfish_battery.o
 obj-$(CONFIG_BATTERY_LEGO_EV3)	+= lego_ev3_battery.o
 obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
+obj-$(CONFIG_BATTERY_QCOM_BATTMGR)	+= qcom_battmgr.o
 obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
 obj-$(CONFIG_BATTERY_SAMSUNG_SDI)	+= samsung-sdi-battery.o
 obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
new file mode 100644
index 000000000000..935c3beb1d01
--- /dev/null
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -0,0 +1,1422 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Linaro Ltd
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/power_supply.h>
+#include <linux/soc/qcom/pdr.h>
+#include <linux/soc/qcom/pmic_glink.h>
+#include <linux/math.h>
+#include <linux/units.h>
+
+#define BATTMGR_CHEMISTRY_LEN	4
+#define BATTMGR_STRING_LEN	128
+
+enum qcom_battmgr_variant {
+	QCOM_BATTMGR_SM8350,
+	QCOM_BATTMGR_SC8280XP,
+};
+
+#define BATTMGR_BAT_STATUS		0x1
+
+#define BATTMGR_REQUEST_NOTIFICATION	0x4
+
+#define BATTMGR_NOTIFICATION		0x7
+#define NOTIF_BAT_PROPERTY		0x30
+#define NOTIF_USB_PROPERTY		0x32
+#define NOTIF_WLS_PROPERTY		0x34
+#define NOTIF_BAT_INFO			0x80
+
+#define BATTMGR_BAT_INFO		0x9
+
+#define BATTMGR_BAT_DISCHARGE_TIME	0xc
+
+#define BATTMGR_BAT_CHARGE_TIME		0xd
+
+#define BATTMGR_BAT_PROPERTY_GET	0x30
+#define BATTMGR_BAT_PROPERTY_SET	0x31
+#define BATT_STATUS			0
+#define BATT_HEALTH			1
+#define BATT_PRESENT			2
+#define BATT_CHG_TYPE			3
+#define BATT_CAPACITY			4
+#define BATT_SOH			5
+#define BATT_VOLT_OCV			6
+#define BATT_VOLT_NOW			7
+#define BATT_VOLT_MAX			8
+#define BATT_CURR_NOW			9
+#define BATT_CHG_CTRL_LIM		10
+#define BATT_CHG_CTRL_LIM_MAX		11
+#define BATT_TEMP			12
+#define BATT_TECHNOLOGY			13
+#define BATT_CHG_COUNTER		14
+#define BATT_CYCLE_COUNT		15
+#define BATT_CHG_FULL_DESIGN		16
+#define BATT_CHG_FULL			17
+#define BATT_MODEL_NAME			18
+#define BATT_TTF_AVG			19
+#define BATT_TTE_AVG			20
+#define BATT_RESISTANCE			21
+#define BATT_POWER_NOW			22
+#define BATT_POWER_AVG			23
+
+#define BATTMGR_USB_PROPERTY_GET	0x32
+#define BATTMGR_USB_PROPERTY_SET	0x33
+#define USB_ONLINE			0
+#define USB_VOLT_NOW			1
+#define USB_VOLT_MAX			2
+#define USB_CURR_NOW			3
+#define USB_CURR_MAX			4
+#define USB_INPUT_CURR_LIMIT		5
+#define USB_TYPE			6
+#define USB_ADAP_TYPE			7
+#define USB_MOISTURE_DET_EN		8
+#define USB_MOISTURE_DET_STS		9
+
+#define BATTMGR_WLS_PROPERTY_GET	0x34
+#define BATTMGR_WLS_PROPERTY_SET	0x35
+#define WLS_ONLINE			0
+#define WLS_VOLT_NOW			1
+#define WLS_VOLT_MAX			2
+#define WLS_CURR_NOW			3
+#define WLS_CURR_MAX			4
+#define WLS_TYPE			5
+#define WLS_BOOST_EN			6
+
+struct qcom_battmgr_enable_request {
+	struct pmic_glink_hdr hdr;
+	__le32 battery_id;
+	__le32 power_state;
+	__le32 low_capacity;
+	__le32 high_capacity;
+};
+
+struct qcom_battmgr_property_request {
+	struct pmic_glink_hdr hdr;
+	__le32 battery;
+	__le32 property;
+	__le32 value;
+};
+
+struct qcom_battmgr_update_request {
+	struct pmic_glink_hdr hdr;
+	u32 battery_id;
+};
+
+struct qcom_battmgr_charge_time_request {
+	struct pmic_glink_hdr hdr;
+	__le32 battery_id;
+	__le32 percent;
+	__le32 reserved;
+};
+
+struct qcom_battmgr_discharge_time_request {
+	struct pmic_glink_hdr hdr;
+	__le32 battery_id;
+	__le32 rate; /* 0 for current rate */
+	__le32 reserved;
+};
+
+struct qcom_battmgr_message {
+	struct pmic_glink_hdr hdr;
+	union {
+		struct {
+			__le32 property;
+			__le32 value;
+			__le32 result;
+		} intval;
+		struct {
+			__le32 property;
+			char model[BATTMGR_STRING_LEN];
+		} strval;
+		struct {
+			/*
+			 * 0: mWh
+			 * 1: mAh
+			 */
+			__le32 power_unit;
+			__le32 design_capacity;
+			__le32 last_full_capacity;
+			/*
+			 * 0 nonrechargable
+			 * 1 rechargable
+			 */
+			__le32 battery_tech;
+			__le32 design_voltage; /* mV */
+			__le32 capacity_low;
+			__le32 capacity_warning;
+			__le32 cycle_count;
+			/* thousandth of persent */
+			__le32 accuracy;
+			__le32 max_sample_time_ms;
+			__le32 min_sample_time_ms;
+			__le32 max_average_interval_ms;
+			__le32 min_average_interval_ms;
+			/* granularity between low and warning */
+			__le32 capacity_granularity1;
+			/* granularity between warning and full */
+			__le32 capacity_granularity2;
+			/*
+			 * 0: no
+			 * 1: cold
+			 * 2: hot
+			 */
+			__le32 swappable;
+			__le32 capabilities;
+			char model_number[BATTMGR_STRING_LEN];
+			char serial_number[BATTMGR_STRING_LEN];
+			char battery_type[BATTMGR_STRING_LEN];
+			char oem_info[BATTMGR_STRING_LEN];
+			char battery_chemistry[BATTMGR_CHEMISTRY_LEN];
+			char uid[BATTMGR_STRING_LEN];
+			__le32 critical_bias;
+			u8 day;
+			u8 month;
+			__le16 year;
+			__le32 battery_id;
+		} info;
+		struct {
+			/*
+			 * BIT(0) discharging
+			 * BIT(1) charging
+			 * BIT(2) critical low
+			 */
+			__le32 battery_state;
+			/* mWh or mAh, based on info->power_unit */
+			__le32 capacity;
+			__le32 rate;
+			/* mv */
+			__le32 battery_voltage;
+			/*
+			 * BIT(0) power online
+			 * BIT(1) discharging
+			 * BIT(2) charging
+			 * BIT(3) battery critical
+			 */
+			__le32 power_state;
+			/*
+			 * 1: AC
+			 * 2: USB
+			 * 3: Wireless
+			 */
+			__le32 charging_source;
+			__le32 temperature;
+		} status;
+		__le32 time;
+		__le32 notification;
+	};
+};
+
+#define BATTMGR_CHARGING_SOURCE_AC	1
+#define BATTMGR_CHARGING_SOURCE_USB	2
+#define BATTMGR_CHARGING_SOURCE_WIRELESS 3
+
+enum qcom_battmgr_unit {
+	QCOM_BATTMGR_UNIT_mWh = 0,
+	QCOM_BATTMGR_UNIT_mAh = 1
+};
+
+struct qcom_battmgr_info {
+	bool valid;
+
+	bool present;
+	unsigned int charge_type;
+	unsigned int design_capacity;
+	unsigned int last_full_capacity;
+	unsigned int voltage_max_design;
+	unsigned int voltage_max;
+	unsigned int capacity_low;
+	unsigned int capacity_warning;
+	unsigned int cycle_count;
+	unsigned int charge_count;
+	char model_number[BATTMGR_STRING_LEN];
+	char serial_number[BATTMGR_STRING_LEN];
+	char oem_info[BATTMGR_STRING_LEN];
+	unsigned char technology;
+	unsigned char day;
+	unsigned char month;
+	unsigned short year;
+};
+
+struct qcom_battmgr_status {
+	unsigned int status;
+	unsigned int health;
+	unsigned int capacity;
+	unsigned int percent;
+	int current_now;
+	int power_now;
+	unsigned int voltage_now;
+	unsigned int voltage_ocv;
+	unsigned int temperature;
+
+	unsigned int discharge_time;
+	unsigned int charge_time;
+};
+
+struct qcom_battmgr_ac {
+	bool online;
+};
+
+struct qcom_battmgr_usb {
+	bool online;
+	unsigned int voltage_now;
+	unsigned int voltage_max;
+	unsigned int current_now;
+	unsigned int current_max;
+	unsigned int current_limit;
+	unsigned int usb_type;
+};
+
+struct qcom_battmgr_wireless {
+	bool online;
+	unsigned int voltage_now;
+	unsigned int voltage_max;
+	unsigned int current_now;
+	unsigned int current_max;
+};
+
+struct qcom_battmgr {
+	struct device *dev;
+	struct pmic_glink_client *client;
+
+	enum qcom_battmgr_variant variant;
+
+	struct power_supply *ac_psy;
+	struct power_supply *bat_psy;
+	struct power_supply *usb_psy;
+	struct power_supply *wls_psy;
+
+	enum qcom_battmgr_unit unit;
+
+	int error;
+	struct completion ack;
+
+	bool service_up;
+
+	struct qcom_battmgr_info info;
+	struct qcom_battmgr_status status;
+	struct qcom_battmgr_ac ac;
+	struct qcom_battmgr_usb usb;
+	struct qcom_battmgr_wireless wireless;
+
+	struct work_struct enable_work;
+
+	/*
+	 * @lock is used to prevent concurrent power supply requests to the
+	 * firmware, as it then stops responding.
+	 */
+	struct mutex lock;
+};
+
+static int qcom_battmgr_request(struct qcom_battmgr *battmgr, void *data, size_t len)
+{
+	unsigned long left;
+	int ret;
+
+	reinit_completion(&battmgr->ack);
+
+	battmgr->error = 0;
+
+	ret = pmic_glink_send(battmgr->client, data, len);
+	if (ret < 0)
+		return ret;
+
+	left = wait_for_completion_timeout(&battmgr->ack, HZ);
+	if (!left)
+		return -ETIMEDOUT;
+
+	return battmgr->error;
+}
+
+static int qcom_battmgr_request_property(struct qcom_battmgr *battmgr, int opcode,
+					 int property, u32 value)
+{
+	struct qcom_battmgr_property_request request = {
+		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
+		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
+		.hdr.opcode = cpu_to_le32(opcode),
+		.battery = cpu_to_le32(0),
+		.property = cpu_to_le32(property),
+		.value = cpu_to_le32(value),
+	};
+
+	return qcom_battmgr_request(battmgr, &request, sizeof(request));
+}
+
+static int qcom_battmgr_update_status(struct qcom_battmgr *battmgr)
+{
+	struct qcom_battmgr_update_request request = {
+		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
+		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
+		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_STATUS),
+		.battery_id = cpu_to_le32(0),
+	};
+
+	return qcom_battmgr_request(battmgr, &request, sizeof(request));
+}
+
+static int qcom_battmgr_update_info(struct qcom_battmgr *battmgr)
+{
+	struct qcom_battmgr_update_request request = {
+		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
+		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
+		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_INFO),
+		.battery_id = cpu_to_le32(0),
+	};
+
+	return qcom_battmgr_request(battmgr, &request, sizeof(request));
+}
+
+static int qcom_battmgr_update_charge_time(struct qcom_battmgr *battmgr)
+{
+	struct qcom_battmgr_charge_time_request request = {
+		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
+		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
+		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_CHARGE_TIME),
+		.battery_id = cpu_to_le32(0),
+		.percent = cpu_to_le32(100),
+	};
+
+	return qcom_battmgr_request(battmgr, &request, sizeof(request));
+}
+
+static int qcom_battmgr_update_discharge_time(struct qcom_battmgr *battmgr)
+{
+	struct qcom_battmgr_discharge_time_request request = {
+		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
+		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
+		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_DISCHARGE_TIME),
+		.battery_id = cpu_to_le32(0),
+		.rate = cpu_to_le32(0),
+	};
+
+	return qcom_battmgr_request(battmgr, &request, sizeof(request));
+}
+
+static const u8 sm8350_bat_prop_map[] = {
+	[POWER_SUPPLY_PROP_STATUS] = BATT_STATUS,
+	[POWER_SUPPLY_PROP_HEALTH] = BATT_HEALTH,
+	[POWER_SUPPLY_PROP_PRESENT] = BATT_PRESENT,
+	[POWER_SUPPLY_PROP_CHARGE_TYPE] = BATT_CHG_TYPE,
+	[POWER_SUPPLY_PROP_CAPACITY] = BATT_CAPACITY,
+	[POWER_SUPPLY_PROP_VOLTAGE_OCV] = BATT_VOLT_OCV,
+	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = BATT_VOLT_NOW,
+	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = BATT_VOLT_MAX,
+	[POWER_SUPPLY_PROP_CURRENT_NOW] = BATT_CURR_NOW,
+	[POWER_SUPPLY_PROP_TEMP] = BATT_TEMP,
+	[POWER_SUPPLY_PROP_TECHNOLOGY] = BATT_TECHNOLOGY,
+	[POWER_SUPPLY_PROP_CHARGE_COUNTER] =  BATT_CHG_COUNTER,
+	[POWER_SUPPLY_PROP_CYCLE_COUNT] = BATT_CYCLE_COUNT,
+	[POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN] =  BATT_CHG_FULL_DESIGN,
+	[POWER_SUPPLY_PROP_CHARGE_FULL] = BATT_CHG_FULL,
+	[POWER_SUPPLY_PROP_MODEL_NAME] = BATT_MODEL_NAME,
+	[POWER_SUPPLY_PROP_TIME_TO_FULL_AVG] = BATT_TTF_AVG,
+	[POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG] = BATT_TTE_AVG,
+	[POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW,
+};
+
+static int qcom_battmgr_bat_sm8350_update(struct qcom_battmgr *battmgr,
+					  enum power_supply_property psp)
+{
+	unsigned int prop;
+	int ret;
+
+	if (psp >= ARRAY_SIZE(sm8350_bat_prop_map))
+		return -EINVAL;
+
+	prop = sm8350_bat_prop_map[psp];
+
+	mutex_lock(&battmgr->lock);
+	ret = qcom_battmgr_request_property(battmgr, BATTMGR_BAT_PROPERTY_GET, prop, 0);
+	mutex_unlock(&battmgr->lock);
+
+	return ret;
+}
+
+static int qcom_battmgr_bat_sc8280xp_update(struct qcom_battmgr *battmgr,
+					    enum power_supply_property psp)
+{
+	int ret;
+
+	mutex_lock(&battmgr->lock);
+
+	if (!battmgr->info.valid) {
+		ret = qcom_battmgr_update_info(battmgr);
+		if (ret < 0)
+			goto out_unlock;
+		battmgr->info.valid = true;
+	}
+
+	ret = qcom_battmgr_update_status(battmgr);
+	if (ret < 0)
+		goto out_unlock;
+
+	if (psp == POWER_SUPPLY_PROP_TIME_TO_FULL_AVG) {
+		ret = qcom_battmgr_update_charge_time(battmgr);
+		if (ret < 0) {
+			ret = -ENODATA;
+			goto out_unlock;
+		}
+	}
+
+	if (psp == POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG) {
+		ret = qcom_battmgr_update_discharge_time(battmgr);
+		if (ret < 0) {
+			ret = -ENODATA;
+			goto out_unlock;
+		}
+	}
+
+out_unlock:
+	mutex_unlock(&battmgr->lock);
+	return ret;
+}
+
+static int qcom_battmgr_bat_get_property(struct power_supply *psy,
+					 enum power_supply_property psp,
+					 union power_supply_propval *val)
+{
+	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
+	enum qcom_battmgr_unit unit = battmgr->unit;
+	int ret;
+
+	if (!battmgr->service_up)
+		return -ENODEV;
+
+	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
+		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
+	else
+		ret = qcom_battmgr_bat_sm8350_update(battmgr, psp);
+	if (ret < 0)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = battmgr->status.status;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = battmgr->info.charge_type;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = battmgr->status.health;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = battmgr->info.present;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = battmgr->info.technology;
+		break;
+	case POWER_SUPPLY_PROP_CYCLE_COUNT:
+		val->intval = battmgr->info.cycle_count;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = battmgr->info.voltage_max_design;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = battmgr->info.voltage_max;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = battmgr->status.voltage_now;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+		val->intval = battmgr->status.voltage_ocv;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = battmgr->status.current_now;
+		break;
+	case POWER_SUPPLY_PROP_POWER_NOW:
+		val->intval = battmgr->status.power_now;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		if (unit != QCOM_BATTMGR_UNIT_mAh)
+			return -ENODATA;
+		val->intval = battmgr->info.design_capacity;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		if (unit != QCOM_BATTMGR_UNIT_mAh)
+			return -ENODATA;
+		val->intval = battmgr->info.last_full_capacity;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_EMPTY:
+		if (unit != QCOM_BATTMGR_UNIT_mAh)
+			return -ENODATA;
+		val->intval = battmgr->info.capacity_low;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		if (unit != QCOM_BATTMGR_UNIT_mAh)
+			return -ENODATA;
+		val->intval = battmgr->status.capacity;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+		val->intval = battmgr->info.charge_count;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+		if (unit != QCOM_BATTMGR_UNIT_mWh)
+			return -ENODATA;
+		val->intval = battmgr->info.design_capacity;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		if (unit != QCOM_BATTMGR_UNIT_mWh)
+			return -ENODATA;
+		val->intval = battmgr->info.last_full_capacity;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
+		if (unit != QCOM_BATTMGR_UNIT_mWh)
+			return -ENODATA;
+		val->intval = battmgr->info.capacity_low;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		if (unit != QCOM_BATTMGR_UNIT_mWh)
+			return -ENODATA;
+		val->intval = battmgr->status.capacity;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = battmgr->status.percent;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = battmgr->status.temperature;
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+		val->intval = battmgr->status.discharge_time;
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+		val->intval = battmgr->status.charge_time;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
+		val->intval = battmgr->info.year;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
+		val->intval = battmgr->info.month;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
+		val->intval = battmgr->info.day;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = battmgr->info.model_number;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = battmgr->info.oem_info;
+		break;
+	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		val->strval = battmgr->info.serial_number;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const enum power_supply_property sc8280xp_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_POWER_NOW,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_EMPTY,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
+	POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
+	POWER_SUPPLY_PROP_MANUFACTURE_DAY,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static const struct power_supply_desc sc8280xp_bat_psy_desc = {
+	.name = "qcom-battmgr-bat",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = sc8280xp_bat_props,
+	.num_properties = ARRAY_SIZE(sc8280xp_bat_props),
+	.get_property = qcom_battmgr_bat_get_property,
+};
+
+static const enum power_supply_property sm8350_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_VOLTAGE_OCV,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_POWER_NOW,
+};
+
+static const struct power_supply_desc sm8350_bat_psy_desc = {
+	.name = "qcom-battmgr-bat",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = sm8350_bat_props,
+	.num_properties = ARRAY_SIZE(sm8350_bat_props),
+	.get_property = qcom_battmgr_bat_get_property,
+};
+
+static int qcom_battmgr_ac_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
+	int ret;
+
+	if (!battmgr->service_up)
+		return -ENODEV;
+
+	ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = battmgr->ac.online;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const enum power_supply_property sc8280xp_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc sc8280xp_ac_psy_desc = {
+	.name = "qcom-battmgr-ac",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.properties = sc8280xp_ac_props,
+	.num_properties = ARRAY_SIZE(sc8280xp_ac_props),
+	.get_property = qcom_battmgr_ac_get_property,
+};
+
+static const u8 sm8350_usb_prop_map[] = {
+	[POWER_SUPPLY_PROP_ONLINE] = USB_ONLINE,
+	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = USB_VOLT_NOW,
+	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = USB_VOLT_MAX,
+	[POWER_SUPPLY_PROP_CURRENT_NOW] = USB_CURR_NOW,
+	[POWER_SUPPLY_PROP_CURRENT_MAX] = USB_CURR_MAX,
+	[POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT] = USB_INPUT_CURR_LIMIT,
+	[POWER_SUPPLY_PROP_USB_TYPE] = USB_TYPE,
+};
+
+static int qcom_battmgr_usb_sm8350_update(struct qcom_battmgr *battmgr,
+					  enum power_supply_property psp)
+{
+	unsigned int prop;
+	int ret;
+
+	if (psp >= ARRAY_SIZE(sm8350_usb_prop_map))
+		return -EINVAL;
+
+	prop = sm8350_usb_prop_map[psp];
+
+	mutex_lock(&battmgr->lock);
+	ret = qcom_battmgr_request_property(battmgr, BATTMGR_USB_PROPERTY_GET, prop, 0);
+	mutex_unlock(&battmgr->lock);
+
+	return ret;
+}
+
+static int qcom_battmgr_usb_get_property(struct power_supply *psy,
+					 enum power_supply_property psp,
+					 union power_supply_propval *val)
+{
+	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
+	int ret;
+
+	if (!battmgr->service_up)
+		return -ENODEV;
+
+	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
+		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
+	else
+		ret = qcom_battmgr_usb_sm8350_update(battmgr, psp);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = battmgr->usb.online;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = battmgr->usb.voltage_now;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = battmgr->usb.voltage_max;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = battmgr->usb.current_now;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = battmgr->usb.current_max;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		val->intval = battmgr->usb.current_limit;
+		break;
+	case POWER_SUPPLY_PROP_USB_TYPE:
+		val->intval = battmgr->usb.usb_type;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const enum power_supply_usb_type usb_psy_supported_types[] = {
+	POWER_SUPPLY_USB_TYPE_UNKNOWN,
+	POWER_SUPPLY_USB_TYPE_SDP,
+	POWER_SUPPLY_USB_TYPE_DCP,
+	POWER_SUPPLY_USB_TYPE_CDP,
+	POWER_SUPPLY_USB_TYPE_ACA,
+	POWER_SUPPLY_USB_TYPE_C,
+	POWER_SUPPLY_USB_TYPE_PD,
+	POWER_SUPPLY_USB_TYPE_PD_DRP,
+	POWER_SUPPLY_USB_TYPE_PD_PPS,
+	POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID,
+};
+
+static const enum power_supply_property sc8280xp_usb_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc sc8280xp_usb_psy_desc = {
+	.name = "qcom-battmgr-usb",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.properties = sc8280xp_usb_props,
+	.num_properties = ARRAY_SIZE(sc8280xp_usb_props),
+	.get_property = qcom_battmgr_usb_get_property,
+	.usb_types = usb_psy_supported_types,
+	.num_usb_types = ARRAY_SIZE(usb_psy_supported_types),
+};
+
+static const enum power_supply_property sm8350_usb_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+	POWER_SUPPLY_PROP_USB_TYPE,
+};
+
+static const struct power_supply_desc sm8350_usb_psy_desc = {
+	.name = "qcom-battmgr-usb",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.properties = sm8350_usb_props,
+	.num_properties = ARRAY_SIZE(sm8350_usb_props),
+	.get_property = qcom_battmgr_usb_get_property,
+	.usb_types = usb_psy_supported_types,
+	.num_usb_types = ARRAY_SIZE(usb_psy_supported_types),
+};
+
+static const u8 sm8350_wls_prop_map[] = {
+	[POWER_SUPPLY_PROP_ONLINE] = WLS_ONLINE,
+	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = WLS_VOLT_NOW,
+	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = WLS_VOLT_MAX,
+	[POWER_SUPPLY_PROP_CURRENT_NOW] = WLS_CURR_NOW,
+	[POWER_SUPPLY_PROP_CURRENT_MAX] = WLS_CURR_MAX,
+};
+
+static int qcom_battmgr_wls_sm8350_update(struct qcom_battmgr *battmgr,
+					  enum power_supply_property psp)
+{
+	unsigned int prop;
+	int ret;
+
+	if (psp >= ARRAY_SIZE(sm8350_wls_prop_map))
+		return -EINVAL;
+
+	prop = sm8350_wls_prop_map[psp];
+
+	mutex_lock(&battmgr->lock);
+	ret = qcom_battmgr_request_property(battmgr, BATTMGR_WLS_PROPERTY_GET, prop, 0);
+	mutex_unlock(&battmgr->lock);
+
+	return ret;
+}
+
+static int qcom_battmgr_wls_get_property(struct power_supply *psy,
+					 enum power_supply_property psp,
+					 union power_supply_propval *val)
+{
+	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
+	int ret;
+
+	if (!battmgr->service_up)
+		return -ENODEV;
+
+	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
+		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
+	else
+		ret = qcom_battmgr_wls_sm8350_update(battmgr, psp);
+	if (ret < 0)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = battmgr->wireless.online;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = battmgr->wireless.voltage_now;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = battmgr->wireless.voltage_max;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = battmgr->wireless.current_now;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = battmgr->wireless.current_max;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const enum power_supply_property sc8280xp_wls_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc sc8280xp_wls_psy_desc = {
+	.name = "qcom-battmgr-wls",
+	.type = POWER_SUPPLY_TYPE_WIRELESS,
+	.properties = sc8280xp_wls_props,
+	.num_properties = ARRAY_SIZE(sc8280xp_wls_props),
+	.get_property = qcom_battmgr_wls_get_property,
+};
+
+static const enum power_supply_property sm8350_wls_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc sm8350_wls_psy_desc = {
+	.name = "qcom-battmgr-wls",
+	.type = POWER_SUPPLY_TYPE_WIRELESS,
+	.properties = sm8350_wls_props,
+	.num_properties = ARRAY_SIZE(sm8350_wls_props),
+	.get_property = qcom_battmgr_wls_get_property,
+};
+
+static void qcom_battmgr_notification(struct qcom_battmgr *battmgr,
+				      const struct qcom_battmgr_message *msg,
+				      int len)
+{
+	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
+	unsigned int notification;
+
+	if (payload_len != sizeof(msg->notification)) {
+		dev_warn(battmgr->dev, "ignoring notification with invalid length\n");
+		return;
+	}
+
+	notification = le32_to_cpu(msg->notification);
+	switch (notification) {
+	case NOTIF_BAT_INFO:
+		battmgr->info.valid = false;
+		fallthrough;
+	case NOTIF_BAT_PROPERTY:
+		power_supply_changed(battmgr->bat_psy);
+		break;
+	case NOTIF_USB_PROPERTY:
+		power_supply_changed(battmgr->usb_psy);
+		break;
+	case NOTIF_WLS_PROPERTY:
+		power_supply_changed(battmgr->wls_psy);
+		break;
+	default:
+		dev_err(battmgr->dev, "unknown notification: %#x\n", notification);
+		break;
+	}
+}
+
+static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src)
+{
+	size_t len = src[0];
+
+	/* Some firmware versions return Pascal-style strings */
+	if (len < BATTMGR_STRING_LEN && len == strnlen(src + 1, BATTMGR_STRING_LEN - 1)) {
+		memcpy(dest, src + 1, len);
+		dest[len] = '\0';
+	} else {
+		memcpy(dest, src, BATTMGR_STRING_LEN);
+	}
+}
+
+static unsigned int qcom_battmgr_sc8280xp_parse_technology(const char *chemistry)
+{
+	if (!strncmp(chemistry, "LIO", BATTMGR_CHEMISTRY_LEN))
+		return POWER_SUPPLY_TECHNOLOGY_LION;
+
+	pr_err("Unknown battery technology '%s'\n", chemistry);
+	return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+}
+
+static unsigned int qcom_battmgr_sc8280xp_convert_temp(unsigned int temperature)
+{
+	return DIV_ROUND_CLOSEST(temperature, 10);
+}
+
+static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
+					   const struct qcom_battmgr_message *resp,
+					   size_t len)
+{
+	unsigned int opcode = le32_to_cpu(resp->hdr.opcode);
+	unsigned int source;
+	unsigned int state;
+	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
+
+	if (payload_len < sizeof(__le32)) {
+		dev_warn(battmgr->dev, "invalid payload length for %#x: %zd\n",
+			 opcode, len);
+		return;
+	}
+
+	switch (opcode) {
+	case BATTMGR_REQUEST_NOTIFICATION:
+		battmgr->error = 0;
+		break;
+	case BATTMGR_BAT_INFO:
+		if (payload_len != sizeof(resp->info)) {
+			dev_warn(battmgr->dev,
+				 "invalid payload length for battery information request: %zd\n",
+				 payload_len);
+			battmgr->error = -ENODATA;
+			return;
+		}
+
+		battmgr->unit = le32_to_cpu(resp->info.power_unit);
+
+		battmgr->info.design_capacity = le32_to_cpu(resp->info.design_capacity) * 1000;
+		battmgr->info.last_full_capacity = le32_to_cpu(resp->info.last_full_capacity) * 1000;
+		battmgr->info.voltage_max_design = le32_to_cpu(resp->info.design_voltage) * 1000;
+		battmgr->info.capacity_low = le32_to_cpu(resp->info.capacity_low) * 1000;
+		battmgr->info.cycle_count = le32_to_cpu(resp->info.cycle_count);
+		qcom_battmgr_sc8280xp_strcpy(battmgr->info.model_number, resp->info.model_number);
+		qcom_battmgr_sc8280xp_strcpy(battmgr->info.serial_number, resp->info.serial_number);
+		battmgr->info.technology = qcom_battmgr_sc8280xp_parse_technology(resp->info.battery_chemistry);
+		qcom_battmgr_sc8280xp_strcpy(battmgr->info.oem_info, resp->info.oem_info);
+		battmgr->info.day = resp->info.day;
+		battmgr->info.month = resp->info.month;
+		battmgr->info.year = le16_to_cpu(resp->info.year);
+		break;
+	case BATTMGR_BAT_STATUS:
+		if (payload_len != sizeof(resp->status)) {
+			dev_warn(battmgr->dev,
+				 "invalid payload length for battery status request: %zd\n",
+				 payload_len);
+			battmgr->error = -ENODATA;
+			return;
+		}
+
+		state = le32_to_cpu(resp->status.battery_state);
+		if (state & BIT(0))
+			battmgr->status.status = POWER_SUPPLY_STATUS_DISCHARGING;
+		else if (state & BIT(1))
+			battmgr->status.status = POWER_SUPPLY_STATUS_CHARGING;
+		else
+			battmgr->status.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+		battmgr->status.capacity = le32_to_cpu(resp->status.capacity) * 1000;
+		battmgr->status.power_now = le32_to_cpu(resp->status.rate) * 1000;
+		battmgr->status.voltage_now = le32_to_cpu(resp->status.battery_voltage) * 1000;
+		battmgr->status.temperature = qcom_battmgr_sc8280xp_convert_temp(le32_to_cpu(resp->status.temperature));
+
+		source = le32_to_cpu(resp->status.charging_source);
+		battmgr->ac.online = source == BATTMGR_CHARGING_SOURCE_AC;
+		battmgr->usb.online = source == BATTMGR_CHARGING_SOURCE_USB;
+		battmgr->wireless.online = source == BATTMGR_CHARGING_SOURCE_WIRELESS;
+		break;
+	case BATTMGR_BAT_DISCHARGE_TIME:
+		battmgr->status.discharge_time = le32_to_cpu(resp->time);
+		break;
+	case BATTMGR_BAT_CHARGE_TIME:
+		battmgr->status.charge_time = le32_to_cpu(resp->time);
+		break;
+	default:
+		dev_warn(battmgr->dev, "unknown message %#x\n", opcode);
+		break;
+	}
+
+	complete(&battmgr->ack);
+}
+
+static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
+					 const struct qcom_battmgr_message *resp,
+					 size_t len)
+{
+	unsigned int property;
+	unsigned int opcode = le32_to_cpu(resp->hdr.opcode);
+	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
+	unsigned int val;
+
+	if (payload_len < sizeof(__le32)) {
+		dev_warn(battmgr->dev, "invalid payload length for %#x: %zd\n",
+			 opcode, len);
+		return;
+	}
+
+	switch (opcode) {
+	case BATTMGR_BAT_PROPERTY_GET:
+		property = le32_to_cpu(resp->intval.property);
+		if (property == BATT_MODEL_NAME) {
+			if (payload_len != sizeof(resp->strval)) {
+				dev_warn(battmgr->dev,
+					 "invalid payload length for BATT_MODEL_NAME request: %zd\n",
+					 payload_len);
+				battmgr->error = -ENODATA;
+				return;
+			}
+		} else {
+			if (payload_len != sizeof(resp->intval)) {
+				dev_warn(battmgr->dev,
+					 "invalid payload length for %#x request: %zd\n",
+					 property, payload_len);
+				battmgr->error = -ENODATA;
+				return;
+			}
+
+			battmgr->error = le32_to_cpu(resp->intval.result);
+			if (battmgr->error)
+				goto out_complete;
+		}
+
+		switch (property) {
+		case BATT_STATUS:
+			battmgr->status.status = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_HEALTH:
+			battmgr->status.health = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_PRESENT:
+			battmgr->info.present = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CHG_TYPE:
+			battmgr->info.charge_type = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CAPACITY:
+			battmgr->status.percent = le32_to_cpu(resp->intval.value);
+			do_div(battmgr->status.percent, 100);
+			break;
+		case BATT_VOLT_OCV:
+			battmgr->status.voltage_ocv = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_VOLT_NOW:
+			battmgr->status.voltage_now = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_VOLT_MAX:
+			battmgr->info.voltage_max = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CURR_NOW:
+			battmgr->status.current_now = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_TEMP:
+			val = le32_to_cpu(resp->intval.value);
+			battmgr->status.temperature = DIV_ROUND_CLOSEST(val, 10);
+			break;
+		case BATT_TECHNOLOGY:
+			battmgr->info.technology = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CHG_COUNTER:
+			battmgr->info.charge_count = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CYCLE_COUNT:
+			battmgr->info.cycle_count = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CHG_FULL_DESIGN:
+			battmgr->info.design_capacity = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_CHG_FULL:
+			battmgr->info.last_full_capacity = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_MODEL_NAME:
+			strscpy(battmgr->info.model_number, resp->strval.model, BATTMGR_STRING_LEN);
+			break;
+		case BATT_TTF_AVG:
+			battmgr->status.charge_time = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_TTE_AVG:
+			battmgr->status.discharge_time = le32_to_cpu(resp->intval.value);
+			break;
+		case BATT_POWER_NOW:
+			battmgr->status.power_now = le32_to_cpu(resp->intval.value);
+			break;
+		default:
+			dev_warn(battmgr->dev, "unknown property %#x\n", property);
+			break;
+		}
+		break;
+	case BATTMGR_USB_PROPERTY_GET:
+		property = le32_to_cpu(resp->intval.property);
+		if (payload_len != sizeof(resp->intval)) {
+			dev_warn(battmgr->dev,
+				 "invalid payload length for %#x request: %zd\n",
+				 property, payload_len);
+			battmgr->error = -ENODATA;
+			return;
+		}
+
+		battmgr->error = le32_to_cpu(resp->intval.result);
+		if (battmgr->error)
+			goto out_complete;
+
+		switch (property) {
+		case USB_ONLINE:
+			battmgr->usb.online = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_VOLT_NOW:
+			battmgr->usb.voltage_now = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_VOLT_MAX:
+			battmgr->usb.voltage_max = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_CURR_NOW:
+			battmgr->usb.current_now = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_CURR_MAX:
+			battmgr->usb.current_max = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_INPUT_CURR_LIMIT:
+			battmgr->usb.current_limit = le32_to_cpu(resp->intval.value);
+			break;
+		case USB_TYPE:
+			battmgr->usb.usb_type = le32_to_cpu(resp->intval.value);
+			break;
+		default:
+			dev_warn(battmgr->dev, "unknown property %#x\n", property);
+			break;
+		}
+		break;
+	case BATTMGR_WLS_PROPERTY_GET:
+		property = le32_to_cpu(resp->intval.property);
+		if (payload_len != sizeof(resp->intval)) {
+			dev_warn(battmgr->dev,
+				 "invalid payload length for %#x request: %zd\n",
+				 property, payload_len);
+			battmgr->error = -ENODATA;
+			return;
+		}
+
+		battmgr->error = le32_to_cpu(resp->intval.result);
+		if (battmgr->error)
+			goto out_complete;
+
+		switch (property) {
+		case WLS_ONLINE:
+			battmgr->wireless.online = le32_to_cpu(resp->intval.value);
+			break;
+		case WLS_VOLT_NOW:
+			battmgr->wireless.voltage_now = le32_to_cpu(resp->intval.value);
+			break;
+		case WLS_VOLT_MAX:
+			battmgr->wireless.voltage_max = le32_to_cpu(resp->intval.value);
+			break;
+		case WLS_CURR_NOW:
+			battmgr->wireless.current_now = le32_to_cpu(resp->intval.value);
+			break;
+		case WLS_CURR_MAX:
+			battmgr->wireless.current_max = le32_to_cpu(resp->intval.value);
+			break;
+		default:
+			dev_warn(battmgr->dev, "unknown property %#x\n", property);
+			break;
+		}
+		break;
+	case BATTMGR_REQUEST_NOTIFICATION:
+		battmgr->error = 0;
+		break;
+	default:
+		dev_warn(battmgr->dev, "unknown message %#x\n", opcode);
+		break;
+	}
+
+out_complete:
+	complete(&battmgr->ack);
+}
+
+static void qcom_battmgr_callback(const void *data, size_t len, void *priv)
+{
+	const struct pmic_glink_hdr *hdr = data;
+	struct qcom_battmgr *battmgr = priv;
+	unsigned int opcode = le32_to_cpu(hdr->opcode);
+
+	if (opcode == BATTMGR_NOTIFICATION)
+		qcom_battmgr_notification(battmgr, data, len);
+	else if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
+		qcom_battmgr_sc8280xp_callback(battmgr, data, len);
+	else
+		qcom_battmgr_sm8350_callback(battmgr, data, len);
+}
+
+static void qcom_battmgr_enable_worker(struct work_struct *work)
+{
+	struct qcom_battmgr *battmgr = container_of(work, struct qcom_battmgr, enable_work);
+	struct qcom_battmgr_enable_request req = {
+		.hdr.owner = PMIC_GLINK_OWNER_BATTMGR,
+		.hdr.type = PMIC_GLINK_NOTIFY,
+		.hdr.opcode = BATTMGR_REQUEST_NOTIFICATION,
+	};
+	int ret;
+
+	ret = qcom_battmgr_request(battmgr, &req, sizeof(req));
+	if (ret)
+		dev_err(battmgr->dev, "failed to request power notifications\n");
+}
+
+static void qcom_battmgr_pdr_notify(void *priv, int state)
+{
+	struct qcom_battmgr *battmgr = priv;
+
+	if (state == SERVREG_SERVICE_STATE_UP) {
+		battmgr->service_up = true;
+		schedule_work(&battmgr->enable_work);
+	} else {
+		battmgr->service_up = false;
+	}
+}
+
+static const struct of_device_id qcom_battmgr_of_variants[] = {
+	{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
+	{ .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
+	/* Unmatched devices falls back to QCOM_BATTMGR_SM8350 */
+	{}
+};
+
+static char *qcom_battmgr_battery[] = { "battery" };
+
+static int qcom_battmgr_probe(struct auxiliary_device *adev,
+			      const struct auxiliary_device_id *id)
+{
+	struct power_supply_config psy_cfg_supply = {};
+	struct power_supply_config psy_cfg = {};
+	const struct of_device_id *match;
+	struct qcom_battmgr *battmgr;
+	struct device *dev = &adev->dev;
+
+	battmgr = devm_kzalloc(dev, sizeof(*battmgr), GFP_KERNEL);
+	if (!battmgr)
+		return -ENOMEM;
+
+	battmgr->dev = dev;
+
+	psy_cfg.drv_data = battmgr;
+	psy_cfg.of_node = adev->dev.of_node;
+
+	psy_cfg_supply.drv_data = battmgr;
+	psy_cfg_supply.of_node = adev->dev.of_node;
+	psy_cfg_supply.supplied_to = qcom_battmgr_battery;
+	psy_cfg_supply.num_supplicants = 1;
+
+	INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker);
+	mutex_init(&battmgr->lock);
+	init_completion(&battmgr->ack);
+
+	match = of_match_device(qcom_battmgr_of_variants, dev->parent);
+	if (match)
+		battmgr->variant = (unsigned long)match->data;
+	else
+		battmgr->variant = QCOM_BATTMGR_SM8350;
+
+	battmgr->client = devm_pmic_glink_register_client(dev,
+							  PMIC_GLINK_OWNER_BATTMGR,
+							  qcom_battmgr_callback,
+							  qcom_battmgr_pdr_notify,
+							  battmgr);
+	if (IS_ERR(battmgr->client))
+		return PTR_ERR(battmgr->client);
+
+	if (battmgr->variant == QCOM_BATTMGR_SC8280XP) {
+		battmgr->bat_psy = devm_power_supply_register(dev, &sc8280xp_bat_psy_desc, &psy_cfg);
+		if (IS_ERR(battmgr->bat_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
+					     "failed to register battery power supply\n");
+
+		battmgr->ac_psy = devm_power_supply_register(dev, &sc8280xp_ac_psy_desc, &psy_cfg_supply);
+		if (IS_ERR(battmgr->ac_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->ac_psy),
+					     "failed to register AC power supply\n");
+
+		battmgr->usb_psy = devm_power_supply_register(dev, &sc8280xp_usb_psy_desc, &psy_cfg_supply);
+		if (IS_ERR(battmgr->usb_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
+					     "failed to register USB power supply\n");
+
+		battmgr->wls_psy = devm_power_supply_register(dev, &sc8280xp_wls_psy_desc, &psy_cfg_supply);
+		if (IS_ERR(battmgr->wls_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
+					     "failed to register wireless charing power supply\n");
+	} else {
+		battmgr->bat_psy = devm_power_supply_register(dev, &sm8350_bat_psy_desc, &psy_cfg);
+		if (IS_ERR(battmgr->bat_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
+					     "failed to register battery power supply\n");
+
+		battmgr->usb_psy = devm_power_supply_register(dev, &sm8350_usb_psy_desc, &psy_cfg_supply);
+		if (IS_ERR(battmgr->usb_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
+					     "failed to register USB power supply\n");
+
+		battmgr->wls_psy = devm_power_supply_register(dev, &sm8350_wls_psy_desc, &psy_cfg_supply);
+		if (IS_ERR(battmgr->wls_psy))
+			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
+					     "failed to register wireless charing power supply\n");
+	}
+
+	dev_set_drvdata(dev, battmgr);
+
+	return 0;
+}
+
+static const struct auxiliary_device_id qcom_battmgr_id_table[] = {
+	{ .name = "pmic_glink.power-supply", },
+	{},
+};
+MODULE_DEVICE_TABLE(auxiliary, qcom_battmgr_id_table);
+
+static struct auxiliary_driver qcom_battmgr_driver = {
+	.name = "pmic_glink_power_supply",
+	.probe = qcom_battmgr_probe,
+	.id_table = qcom_battmgr_id_table,
+};
+
+static int __init qcom_battmgr_init(void)
+{
+	return auxiliary_driver_register(&qcom_battmgr_driver);
+}
+module_init(qcom_battmgr_init);
+
+static void __exit qcom_battmgr_exit(void)
+{
+	auxiliary_driver_unregister(&qcom_battmgr_driver);
+}
+module_exit(qcom_battmgr_exit);
+
+MODULE_DESCRIPTION("Qualcomm PMIC GLINK battery manager driver");
+MODULE_LICENSE("GPL");
-- 
2.35.1


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

* Re: [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding
  2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
@ 2022-08-18  7:55   ` Krzysztof Kozlowski
  2022-08-22 12:39   ` Rob Herring
  1 sibling, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2022-08-18  7:55 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Sebastian Reichel
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm

On 18/08/2022 06:15, Bjorn Andersson wrote:
> The PMIC GLINK service, running on a coprocessor on some modern Qualcomm
> platforms and implement USB Type-C handling and battery management.
> This binding describes the component in the OS used to communicate with
> the firmware and connect it's resources to those described in the
> Devicetree, particularly the USB Type-C controllers relationship with
> USB and DisplayPort components.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  .../bindings/soc/qcom/qcom,pmic-glink.yaml    | 98 +++++++++++++++++++


Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>


Best regards,
Krzysztof

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

* Re: [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
@ 2022-08-18  8:05   ` Krzysztof Kozlowski
  2022-10-24 20:02   ` Andrew Halaney
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2022-08-18  8:05 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross, Konrad Dybcio
  Cc: Rob Herring, Krzysztof Kozlowski, Sebastian Reichel,
	linux-arm-msm, devicetree, linux-kernel, linux-pm

On 18/08/2022 06:15, Bjorn Andersson wrote:
> The PMIC GLINK service runs on one of the co-processors of some modern
> Qualcomm platforms and implements USB-C and battery managements. It uses
> a message based protocol over GLINK for communication with the OS, hence
> the name.
> 
> The driver implemented provides the rpmsg device for communication and
> uses auxilirary bus to spawn off individual devices in respsective

typos:
auxiliary
respective
> subsystem. The auxilirary devices are spawned off from a

auxiliary

> platform_device, so that the drm_bridge is available early, to allow the
> DisplayPort driver to probe even before the remoteproc has spun up.
> 

(...)

> +
> +static int pmic_glink_init(void)
> +{
> +	platform_driver_register(&pmic_glink_driver);
> +	register_rpmsg_driver(&pmic_glink_rpmsg_driver);
> +
> +	return 0;
> +};
> +module_init(pmic_glink_init);
> +
> +static void pmic_glink_exit(void)
> +{
> +	platform_driver_unregister(&pmic_glink_driver);
> +	unregister_rpmsg_driver(&pmic_glink_rpmsg_driver);

Shouldn't this be in reversed order of init()? So first unregister
rpmsg, then platform driver.

> +};
> +module_exit(pmic_glink_exit);
> +
> +MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h
> new file mode 100644
> index 000000000000..40470f8dfc1e
> --- /dev/null
> +++ b/include/linux/soc/qcom/pmic_glink.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2022, Linaro Ltd
> + */
> +#ifndef __PMIC_GLINK_H__

I propose more detailed guard:
__SOC_QCOM_PMIC_GLINK_H__

> +#define __PMIC_GLINK_H__


Best regards,
Krzysztof

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

* Re: [PATCH 0/4] soc: qcom: Introduce PMIC GLINK
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
                   ` (3 preceding siblings ...)
  2022-08-18  3:15 ` [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply Bjorn Andersson
@ 2022-08-18 19:25 ` Steev Klimaszewski
  2023-02-06 22:30 ` Bjorn Andersson
  5 siblings, 0 replies; 18+ messages in thread
From: Steev Klimaszewski @ 2022-08-18 19:25 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Sebastian Reichel
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm


On 8/17/22 10:15 PM, Bjorn Andersson wrote:
> This implements the base PMIC GLINK driver, a power_supply driver and a driver
> for the USB Type-C altmode protocol. This has been tested and shown to provide
> battery information, USB Type-C switch and mux requests and DisplayPort
> notifications on SC8180X, SC8280XP and SM8350.
>
> Bjorn Andersson (4):
>    dt-bindings: soc: qcom: Introduce PMIC GLINK binding
>    soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
>    soc: qcom: pmic_glink: Introduce altmode support
>    power: supply: Introduce Qualcomm PMIC GLINK power supply
>
>   .../bindings/soc/qcom/qcom,pmic-glink.yaml    |   98 ++
>   drivers/power/supply/Kconfig                  |    9 +
>   drivers/power/supply/Makefile                 |    1 +
>   drivers/power/supply/qcom_battmgr.c           | 1422 +++++++++++++++++
>   drivers/soc/qcom/Kconfig                      |   14 +
>   drivers/soc/qcom/Makefile                     |    2 +
>   drivers/soc/qcom/pmic_glink.c                 |  336 ++++
>   drivers/soc/qcom/pmic_glink_altmode.c         |  477 ++++++
>   include/linux/soc/qcom/pmic_glink.h           |   32 +
>   9 files changed, 2391 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
>   create mode 100644 drivers/power/supply/qcom_battmgr.c
>   create mode 100644 drivers/soc/qcom/pmic_glink.c
>   create mode 100644 drivers/soc/qcom/pmic_glink_altmode.c
>   create mode 100644 include/linux/soc/qcom/pmic_glink.h
>
Tested-by: Steev Klimaszewski <steev@kali.org> #Thinkpad X13s

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

* Re: [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding
  2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
  2022-08-18  7:55   ` Krzysztof Kozlowski
@ 2022-08-22 12:39   ` Rob Herring
  1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring @ 2022-08-22 12:39 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

On Wed, Aug 17, 2022 at 08:15:09PM -0700, Bjorn Andersson wrote:
> The PMIC GLINK service, running on a coprocessor on some modern Qualcomm
> platforms and implement USB Type-C handling and battery management.
> This binding describes the component in the OS used to communicate with
> the firmware and connect it's resources to those described in the
> Devicetree, particularly the USB Type-C controllers relationship with
> USB and DisplayPort components.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  .../bindings/soc/qcom/qcom,pmic-glink.yaml    | 98 +++++++++++++++++++
>  1 file changed, 98 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
> 
> diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
> new file mode 100644
> index 000000000000..3261f9d27a47
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml
> @@ -0,0 +1,98 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/soc/qcom/qcom,pmic-glink.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm PMIC GLINK firmware interface for battery management, USB
> +  Type-C and other things.
> +
> +maintainers:
> +  - Bjorn Andersson <bjorn.andersson@linaro.org>
> +
> +description:
> +  The PMIC GLINK service, running on a coprocessor on some modern Qualcomm
> +  platforms and implement USB Type-C handling and battery management. This
> +  binding describes the component in the OS used to communicate with the
> +  firmware and connect it's resources to those described in the Devicetree,
> +  particularly the USB Type-C controllers relationship with USB and DisplayPort
> +  components.
> +
> +properties:
> +  compatible:
> +    items:
> +      - enum:
> +          - qcom,sc8180x-pmic-glink
> +          - qcom,sc8280xp-pmic-glink
> +          - qcom,sm8350-pmic-glink
> +      - const: qcom,pmic-glink
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 0
> +
> +patternProperties:
> +  '^connector@\d$':
> +    $ref: /schemas/connector/usb-connector.yaml#
> +    unevaluatedProperties: false
> +
> +required:
> +  - compatible
> +
> +additionalProperties: false
> +
> +examples:
> +  - |+
> +    pmic-glink {
> +        compatible = "qcom,sc8280xp-pmic-glink", "qcom,pmic-glink";
> +
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        connector@0 {
> +            compatible = "usb-c-connector";
> +            reg = <0>;

'reg' causes a warning:

Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.example.dtb: pmic-glink: connector@0: Unevaluated properties are not allowed ('reg' was unexpected)

> +            power-role = "dual";
> +            data-role = "dual";
> +
> +            ports {
> +                #address-cells = <1>;
> +                #size-cells = <0>;
> +
> +                port@0 {
> +                    reg = <0>;
> +                    endpoint {
> +                        remote-endpoint = <&usb_role>;
> +                    };
> +                };
> +
> +                port@1 {
> +                    reg = <1>;
> +
> +                    #address-cells = <1>;
> +                    #size-cells = <0>;
> +
> +                    endpoint@0 {
> +                        reg = <0>;
> +                        remote-endpoint = <&qmp_out>;
> +                    };
> +
> +                    endpoint@1 {
> +                        reg = <1>;
> +                        remote-endpoint = <&displayport_hpd>;
> +                    };
> +                };
> +
> +                port@2 {
> +                    reg = <2>;
> +                    endpoint {
> +                        remote-endpoint = <&sbu_mux>;
> +                    };
> +                };
> +            };
> +        };
> +    };
> +...
> +
> -- 
> 2.35.1
> 
> 

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

* Re: [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply
  2022-08-18  3:15 ` [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply Bjorn Andersson
@ 2022-08-30 15:25   ` Johan Hovold
  2022-09-20  2:49   ` Steev Klimaszewski
  1 sibling, 0 replies; 18+ messages in thread
From: Johan Hovold @ 2022-08-30 15:25 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Konrad Dybcio, Sebastian Reichel, Andy Gross, Rob Herring,
	Krzysztof Kozlowski, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

On Wed, Aug 17, 2022 at 08:15:12PM -0700, Bjorn Andersson wrote:
> The PMIC GLINK service, running on a coprocessor of modern Qualcomm
> platforms, deals with battery charging and fuel gauging, as well as
> reporting status of AC and wireless power supplies.
> 
> As this is just one of the functionalities provided by the PMIC GLINK
> service, this power supply driver is implemented as an auxilirary bus
> driver, spawned by the main "pmic glink" driver when the PMIC GLINK
> service is detected.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---

> +static int qcom_battmgr_request(struct qcom_battmgr *battmgr, void *data, size_t len)
> +{
> +	unsigned long left;
> +	int ret;
> +
> +	reinit_completion(&battmgr->ack);
> +
> +	battmgr->error = 0;
> +
> +	ret = pmic_glink_send(battmgr->client, data, len);
> +	if (ret < 0)
> +		return ret;
> +
> +	left = wait_for_completion_timeout(&battmgr->ack, HZ);
> +	if (!left)
> +		return -ETIMEDOUT;
> +
> +	return battmgr->error;
> +}

> +static void qcom_battmgr_notification(struct qcom_battmgr *battmgr,
> +				      const struct qcom_battmgr_message *msg,
> +				      int len)
> +{
> +	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
> +	unsigned int notification;
> +
> +	if (payload_len != sizeof(msg->notification)) {
> +		dev_warn(battmgr->dev, "ignoring notification with invalid length\n");
> +		return;
> +	}
> +
> +	notification = le32_to_cpu(msg->notification);
> +	switch (notification) {
> +	case NOTIF_BAT_INFO:
> +		battmgr->info.valid = false;
> +		fallthrough;
> +	case NOTIF_BAT_PROPERTY:
> +		power_supply_changed(battmgr->bat_psy);
> +		break;
> +	case NOTIF_USB_PROPERTY:
> +		power_supply_changed(battmgr->usb_psy);
> +		break;
> +	case NOTIF_WLS_PROPERTY:
> +		power_supply_changed(battmgr->wls_psy);
> +		break;
> +	default:
> +		dev_err(battmgr->dev, "unknown notification: %#x\n", notification);
> +		break;
> +	}
> +}

> +static void qcom_battmgr_callback(const void *data, size_t len, void *priv)
> +{
> +	const struct pmic_glink_hdr *hdr = data;
> +	struct qcom_battmgr *battmgr = priv;
> +	unsigned int opcode = le32_to_cpu(hdr->opcode);
> +
> +	if (opcode == BATTMGR_NOTIFICATION)
> +		qcom_battmgr_notification(battmgr, data, len);
> +	else if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
> +		qcom_battmgr_sc8280xp_callback(battmgr, data, len);
> +	else
> +		qcom_battmgr_sm8350_callback(battmgr, data, len);
> +}
> +
> +static void qcom_battmgr_enable_worker(struct work_struct *work)
> +{
> +	struct qcom_battmgr *battmgr = container_of(work, struct qcom_battmgr, enable_work);
> +	struct qcom_battmgr_enable_request req = {
> +		.hdr.owner = PMIC_GLINK_OWNER_BATTMGR,
> +		.hdr.type = PMIC_GLINK_NOTIFY,
> +		.hdr.opcode = BATTMGR_REQUEST_NOTIFICATION,
> +	};
> +	int ret;
> +
> +	ret = qcom_battmgr_request(battmgr, &req, sizeof(req));
> +	if (ret)
> +		dev_err(battmgr->dev, "failed to request power notifications\n");
> +}
> +
> +static void qcom_battmgr_pdr_notify(void *priv, int state)
> +{
> +	struct qcom_battmgr *battmgr = priv;
> +
> +	if (state == SERVREG_SERVICE_STATE_UP) {
> +		battmgr->service_up = true;
> +		schedule_work(&battmgr->enable_work);
> +	} else {
> +		battmgr->service_up = false;
> +	}
> +}
> +
> +static const struct of_device_id qcom_battmgr_of_variants[] = {
> +	{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
> +	{ .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
> +	/* Unmatched devices falls back to QCOM_BATTMGR_SM8350 */
> +	{}
> +};
> +
> +static char *qcom_battmgr_battery[] = { "battery" };
> +
> +static int qcom_battmgr_probe(struct auxiliary_device *adev,
> +			      const struct auxiliary_device_id *id)
> +{
> +	struct power_supply_config psy_cfg_supply = {};
> +	struct power_supply_config psy_cfg = {};
> +	const struct of_device_id *match;
> +	struct qcom_battmgr *battmgr;
> +	struct device *dev = &adev->dev;
> +
> +	battmgr = devm_kzalloc(dev, sizeof(*battmgr), GFP_KERNEL);
> +	if (!battmgr)
> +		return -ENOMEM;
> +
> +	battmgr->dev = dev;
> +
> +	psy_cfg.drv_data = battmgr;
> +	psy_cfg.of_node = adev->dev.of_node;
> +
> +	psy_cfg_supply.drv_data = battmgr;
> +	psy_cfg_supply.of_node = adev->dev.of_node;
> +	psy_cfg_supply.supplied_to = qcom_battmgr_battery;
> +	psy_cfg_supply.num_supplicants = 1;
> +
> +	INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker);
> +	mutex_init(&battmgr->lock);
> +	init_completion(&battmgr->ack);
> +
> +	match = of_match_device(qcom_battmgr_of_variants, dev->parent);
> +	if (match)
> +		battmgr->variant = (unsigned long)match->data;
> +	else
> +		battmgr->variant = QCOM_BATTMGR_SM8350;
> +
> +	battmgr->client = devm_pmic_glink_register_client(dev,
> +							  PMIC_GLINK_OWNER_BATTMGR,
> +							  qcom_battmgr_callback,
> +							  qcom_battmgr_pdr_notify,
> +							  battmgr);
> +	if (IS_ERR(battmgr->client))
> +		return PTR_ERR(battmgr->client);

This is racy as you register the callbacks before registering the power
supplies below.

I've seen NULL derefs in qcom_battmgr_notification() when trying to
access the power supplies before they have been allocated due to early
notifications on both CRD and X13s. This can easily be reproduced by
adding some sleep here.

On the other hand, I guess you can't just move the callback registration
after registering the supplies as battmgr->client is needed to process
requests.

> +
> +	if (battmgr->variant == QCOM_BATTMGR_SC8280XP) {
> +		battmgr->bat_psy = devm_power_supply_register(dev, &sc8280xp_bat_psy_desc, &psy_cfg);
> +		if (IS_ERR(battmgr->bat_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
> +					     "failed to register battery power supply\n");
> +
> +		battmgr->ac_psy = devm_power_supply_register(dev, &sc8280xp_ac_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->ac_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->ac_psy),
> +					     "failed to register AC power supply\n");
> +
> +		battmgr->usb_psy = devm_power_supply_register(dev, &sc8280xp_usb_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->usb_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
> +					     "failed to register USB power supply\n");
> +
> +		battmgr->wls_psy = devm_power_supply_register(dev, &sc8280xp_wls_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->wls_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
> +					     "failed to register wireless charing power supply\n");
> +	} else {
> +		battmgr->bat_psy = devm_power_supply_register(dev, &sm8350_bat_psy_desc, &psy_cfg);
> +		if (IS_ERR(battmgr->bat_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
> +					     "failed to register battery power supply\n");
> +
> +		battmgr->usb_psy = devm_power_supply_register(dev, &sm8350_usb_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->usb_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
> +					     "failed to register USB power supply\n");
> +
> +		battmgr->wls_psy = devm_power_supply_register(dev, &sm8350_wls_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->wls_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
> +					     "failed to register wireless charing power supply\n");
> +	}
> +
> +	dev_set_drvdata(dev, battmgr);

You never use the driver data so you shouldn't set it.

> +
> +	return 0;
> +}

Johan

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

* Re: [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply
  2022-08-18  3:15 ` [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply Bjorn Andersson
  2022-08-30 15:25   ` Johan Hovold
@ 2022-09-20  2:49   ` Steev Klimaszewski
  1 sibling, 0 replies; 18+ messages in thread
From: Steev Klimaszewski @ 2022-09-20  2:49 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Sebastian Reichel
  Cc: Andy Gross, Rob Herring, Krzysztof Kozlowski, linux-arm-msm,
	devicetree, linux-kernel, linux-pm

Hi Bjorn,

On 8/17/22 10:15 PM, Bjorn Andersson wrote:
> The PMIC GLINK service, running on a coprocessor of modern Qualcomm
> platforms, deals with battery charging and fuel gauging, as well as
> reporting status of AC and wireless power supplies.
>
> As this is just one of the functionalities provided by the PMIC GLINK
> service, this power supply driver is implemented as an auxilirary bus
> driver, spawned by the main "pmic glink" driver when the PMIC GLINK
> service is detected.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>   drivers/power/supply/Kconfig        |    9 +
>   drivers/power/supply/Makefile       |    1 +
>   drivers/power/supply/qcom_battmgr.c | 1422 +++++++++++++++++++++++++++
>   3 files changed, 1432 insertions(+)
>   create mode 100644 drivers/power/supply/qcom_battmgr.c
>
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 1aa8323ad9f6..490386ae729f 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -183,6 +183,15 @@ config BATTERY_PMU
>   	  Say Y here to expose battery information on Apple machines
>   	  through the generic battery class.
>   
> +config BATTERY_QCOM_BATTMGR
> +	tristate "Qualcomm PMIC GLINK battery manager support"
> +	depends on QCOM_PMIC_GLINK
> +	select AUXILIARY_BUS
> +	help
> +	  Say Y here to enable the Qualcomm PMIC GLINK power supply driver,
> +	  which is used on modern Qualcomm platforms to provide battery and
> +	  power supply information.
> +
>   config BATTERY_OLPC
>   	tristate "One Laptop Per Child battery"
>   	depends on OLPC_EC
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index 7f02f36aea55..f93d777487f6 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_GAUGE_LTC2941)	+= ltc2941-battery-gauge.o
>   obj-$(CONFIG_BATTERY_GOLDFISH)	+= goldfish_battery.o
>   obj-$(CONFIG_BATTERY_LEGO_EV3)	+= lego_ev3_battery.o
>   obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
> +obj-$(CONFIG_BATTERY_QCOM_BATTMGR)	+= qcom_battmgr.o
>   obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
>   obj-$(CONFIG_BATTERY_SAMSUNG_SDI)	+= samsung-sdi-battery.o
>   obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
> diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
> new file mode 100644
> index 000000000000..935c3beb1d01
> --- /dev/null
> +++ b/drivers/power/supply/qcom_battmgr.c
> @@ -0,0 +1,1422 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2022, Linaro Ltd
> + */
> +#include <linux/auxiliary_bus.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/soc/qcom/pdr.h>
> +#include <linux/soc/qcom/pmic_glink.h>
> +#include <linux/math.h>
> +#include <linux/units.h>
> +
> +#define BATTMGR_CHEMISTRY_LEN	4
> +#define BATTMGR_STRING_LEN	128
> +
> +enum qcom_battmgr_variant {
> +	QCOM_BATTMGR_SM8350,
> +	QCOM_BATTMGR_SC8280XP,
> +};
> +
> +#define BATTMGR_BAT_STATUS		0x1
> +
> +#define BATTMGR_REQUEST_NOTIFICATION	0x4
> +
> +#define BATTMGR_NOTIFICATION		0x7
> +#define NOTIF_BAT_PROPERTY		0x30
> +#define NOTIF_USB_PROPERTY		0x32
> +#define NOTIF_WLS_PROPERTY		0x34
> +#define NOTIF_BAT_INFO			0x80
> +
> +#define BATTMGR_BAT_INFO		0x9
> +
> +#define BATTMGR_BAT_DISCHARGE_TIME	0xc
> +
> +#define BATTMGR_BAT_CHARGE_TIME		0xd
> +
> +#define BATTMGR_BAT_PROPERTY_GET	0x30
> +#define BATTMGR_BAT_PROPERTY_SET	0x31
> +#define BATT_STATUS			0
> +#define BATT_HEALTH			1
> +#define BATT_PRESENT			2
> +#define BATT_CHG_TYPE			3
> +#define BATT_CAPACITY			4
> +#define BATT_SOH			5
> +#define BATT_VOLT_OCV			6
> +#define BATT_VOLT_NOW			7
> +#define BATT_VOLT_MAX			8
> +#define BATT_CURR_NOW			9
> +#define BATT_CHG_CTRL_LIM		10
> +#define BATT_CHG_CTRL_LIM_MAX		11
> +#define BATT_TEMP			12
> +#define BATT_TECHNOLOGY			13
> +#define BATT_CHG_COUNTER		14
> +#define BATT_CYCLE_COUNT		15
> +#define BATT_CHG_FULL_DESIGN		16
> +#define BATT_CHG_FULL			17
> +#define BATT_MODEL_NAME			18
> +#define BATT_TTF_AVG			19
> +#define BATT_TTE_AVG			20
> +#define BATT_RESISTANCE			21
> +#define BATT_POWER_NOW			22
> +#define BATT_POWER_AVG			23
> +
> +#define BATTMGR_USB_PROPERTY_GET	0x32
> +#define BATTMGR_USB_PROPERTY_SET	0x33
> +#define USB_ONLINE			0
> +#define USB_VOLT_NOW			1
> +#define USB_VOLT_MAX			2
> +#define USB_CURR_NOW			3
> +#define USB_CURR_MAX			4
> +#define USB_INPUT_CURR_LIMIT		5
> +#define USB_TYPE			6
> +#define USB_ADAP_TYPE			7
> +#define USB_MOISTURE_DET_EN		8
> +#define USB_MOISTURE_DET_STS		9
> +
> +#define BATTMGR_WLS_PROPERTY_GET	0x34
> +#define BATTMGR_WLS_PROPERTY_SET	0x35
> +#define WLS_ONLINE			0
> +#define WLS_VOLT_NOW			1
> +#define WLS_VOLT_MAX			2
> +#define WLS_CURR_NOW			3
> +#define WLS_CURR_MAX			4
> +#define WLS_TYPE			5
> +#define WLS_BOOST_EN			6
> +
> +struct qcom_battmgr_enable_request {
> +	struct pmic_glink_hdr hdr;
> +	__le32 battery_id;
> +	__le32 power_state;
> +	__le32 low_capacity;
> +	__le32 high_capacity;
> +};
> +
> +struct qcom_battmgr_property_request {
> +	struct pmic_glink_hdr hdr;
> +	__le32 battery;
> +	__le32 property;
> +	__le32 value;
> +};
> +
> +struct qcom_battmgr_update_request {
> +	struct pmic_glink_hdr hdr;
> +	u32 battery_id;
> +};
> +
> +struct qcom_battmgr_charge_time_request {
> +	struct pmic_glink_hdr hdr;
> +	__le32 battery_id;
> +	__le32 percent;
> +	__le32 reserved;
> +};
> +
> +struct qcom_battmgr_discharge_time_request {
> +	struct pmic_glink_hdr hdr;
> +	__le32 battery_id;
> +	__le32 rate; /* 0 for current rate */
> +	__le32 reserved;
> +};
> +
> +struct qcom_battmgr_message {
> +	struct pmic_glink_hdr hdr;
> +	union {
> +		struct {
> +			__le32 property;
> +			__le32 value;
> +			__le32 result;
> +		} intval;
> +		struct {
> +			__le32 property;
> +			char model[BATTMGR_STRING_LEN];
> +		} strval;
> +		struct {
> +			/*
> +			 * 0: mWh
> +			 * 1: mAh
> +			 */
> +			__le32 power_unit;
> +			__le32 design_capacity;
> +			__le32 last_full_capacity;
> +			/*
> +			 * 0 nonrechargable
> +			 * 1 rechargable
> +			 */
> +			__le32 battery_tech;
> +			__le32 design_voltage; /* mV */
> +			__le32 capacity_low;
> +			__le32 capacity_warning;
> +			__le32 cycle_count;
> +			/* thousandth of persent */
> +			__le32 accuracy;
> +			__le32 max_sample_time_ms;
> +			__le32 min_sample_time_ms;
> +			__le32 max_average_interval_ms;
> +			__le32 min_average_interval_ms;
> +			/* granularity between low and warning */
> +			__le32 capacity_granularity1;
> +			/* granularity between warning and full */
> +			__le32 capacity_granularity2;
> +			/*
> +			 * 0: no
> +			 * 1: cold
> +			 * 2: hot
> +			 */
> +			__le32 swappable;
> +			__le32 capabilities;
> +			char model_number[BATTMGR_STRING_LEN];
> +			char serial_number[BATTMGR_STRING_LEN];
> +			char battery_type[BATTMGR_STRING_LEN];
> +			char oem_info[BATTMGR_STRING_LEN];
> +			char battery_chemistry[BATTMGR_CHEMISTRY_LEN];
> +			char uid[BATTMGR_STRING_LEN];
> +			__le32 critical_bias;
> +			u8 day;
> +			u8 month;
> +			__le16 year;
> +			__le32 battery_id;
> +		} info;
> +		struct {
> +			/*
> +			 * BIT(0) discharging
> +			 * BIT(1) charging
> +			 * BIT(2) critical low
> +			 */
> +			__le32 battery_state;
> +			/* mWh or mAh, based on info->power_unit */
> +			__le32 capacity;
> +			__le32 rate;
> +			/* mv */
> +			__le32 battery_voltage;
> +			/*
> +			 * BIT(0) power online
> +			 * BIT(1) discharging
> +			 * BIT(2) charging
> +			 * BIT(3) battery critical
> +			 */
> +			__le32 power_state;
> +			/*
> +			 * 1: AC
> +			 * 2: USB
> +			 * 3: Wireless
> +			 */
> +			__le32 charging_source;
> +			__le32 temperature;
> +		} status;
> +		__le32 time;
> +		__le32 notification;
> +	};
> +};
> +
> +#define BATTMGR_CHARGING_SOURCE_AC	1
> +#define BATTMGR_CHARGING_SOURCE_USB	2
> +#define BATTMGR_CHARGING_SOURCE_WIRELESS 3
> +
> +enum qcom_battmgr_unit {
> +	QCOM_BATTMGR_UNIT_mWh = 0,
> +	QCOM_BATTMGR_UNIT_mAh = 1
> +};
> +
> +struct qcom_battmgr_info {
> +	bool valid;
> +
> +	bool present;
> +	unsigned int charge_type;
> +	unsigned int design_capacity;
> +	unsigned int last_full_capacity;
> +	unsigned int voltage_max_design;
> +	unsigned int voltage_max;
> +	unsigned int capacity_low;
> +	unsigned int capacity_warning;
> +	unsigned int cycle_count;
> +	unsigned int charge_count;
> +	char model_number[BATTMGR_STRING_LEN];
> +	char serial_number[BATTMGR_STRING_LEN];
> +	char oem_info[BATTMGR_STRING_LEN];
> +	unsigned char technology;
> +	unsigned char day;
> +	unsigned char month;
> +	unsigned short year;
> +};
> +
> +struct qcom_battmgr_status {
> +	unsigned int status;
> +	unsigned int health;
> +	unsigned int capacity;
> +	unsigned int percent;
> +	int current_now;
> +	int power_now;
> +	unsigned int voltage_now;
> +	unsigned int voltage_ocv;
> +	unsigned int temperature;
> +
> +	unsigned int discharge_time;
> +	unsigned int charge_time;
> +};
> +
> +struct qcom_battmgr_ac {
> +	bool online;
> +};
> +
> +struct qcom_battmgr_usb {
> +	bool online;
> +	unsigned int voltage_now;
> +	unsigned int voltage_max;
> +	unsigned int current_now;
> +	unsigned int current_max;
> +	unsigned int current_limit;
> +	unsigned int usb_type;
> +};
> +
> +struct qcom_battmgr_wireless {
> +	bool online;
> +	unsigned int voltage_now;
> +	unsigned int voltage_max;
> +	unsigned int current_now;
> +	unsigned int current_max;
> +};
> +
> +struct qcom_battmgr {
> +	struct device *dev;
> +	struct pmic_glink_client *client;
> +
> +	enum qcom_battmgr_variant variant;
> +
> +	struct power_supply *ac_psy;
> +	struct power_supply *bat_psy;
> +	struct power_supply *usb_psy;
> +	struct power_supply *wls_psy;
> +
> +	enum qcom_battmgr_unit unit;
> +
> +	int error;
> +	struct completion ack;
> +
> +	bool service_up;
> +
> +	struct qcom_battmgr_info info;
> +	struct qcom_battmgr_status status;
> +	struct qcom_battmgr_ac ac;
> +	struct qcom_battmgr_usb usb;
> +	struct qcom_battmgr_wireless wireless;
> +
> +	struct work_struct enable_work;
> +
> +	/*
> +	 * @lock is used to prevent concurrent power supply requests to the
> +	 * firmware, as it then stops responding.
> +	 */
> +	struct mutex lock;
> +};
> +
> +static int qcom_battmgr_request(struct qcom_battmgr *battmgr, void *data, size_t len)
> +{
> +	unsigned long left;
> +	int ret;
> +
> +	reinit_completion(&battmgr->ack);
> +
> +	battmgr->error = 0;
> +
> +	ret = pmic_glink_send(battmgr->client, data, len);
> +	if (ret < 0)
> +		return ret;
> +
> +	left = wait_for_completion_timeout(&battmgr->ack, HZ);
> +	if (!left)
> +		return -ETIMEDOUT;
> +
> +	return battmgr->error;
> +}
> +
> +static int qcom_battmgr_request_property(struct qcom_battmgr *battmgr, int opcode,
> +					 int property, u32 value)
> +{
> +	struct qcom_battmgr_property_request request = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(opcode),
> +		.battery = cpu_to_le32(0),
> +		.property = cpu_to_le32(property),
> +		.value = cpu_to_le32(value),
> +	};
> +
> +	return qcom_battmgr_request(battmgr, &request, sizeof(request));
> +}
> +
> +static int qcom_battmgr_update_status(struct qcom_battmgr *battmgr)
> +{
> +	struct qcom_battmgr_update_request request = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_STATUS),
> +		.battery_id = cpu_to_le32(0),
> +	};
> +
> +	return qcom_battmgr_request(battmgr, &request, sizeof(request));
> +}
> +
> +static int qcom_battmgr_update_info(struct qcom_battmgr *battmgr)
> +{
> +	struct qcom_battmgr_update_request request = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_INFO),
> +		.battery_id = cpu_to_le32(0),
> +	};
> +
> +	return qcom_battmgr_request(battmgr, &request, sizeof(request));
> +}
> +
> +static int qcom_battmgr_update_charge_time(struct qcom_battmgr *battmgr)
> +{
> +	struct qcom_battmgr_charge_time_request request = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_CHARGE_TIME),
> +		.battery_id = cpu_to_le32(0),
> +		.percent = cpu_to_le32(100),
> +	};
> +
> +	return qcom_battmgr_request(battmgr, &request, sizeof(request));
> +}
> +
> +static int qcom_battmgr_update_discharge_time(struct qcom_battmgr *battmgr)
> +{
> +	struct qcom_battmgr_discharge_time_request request = {
> +		.hdr.owner = cpu_to_le32(PMIC_GLINK_OWNER_BATTMGR),
> +		.hdr.type = cpu_to_le32(PMIC_GLINK_REQ_RESP),
> +		.hdr.opcode = cpu_to_le32(BATTMGR_BAT_DISCHARGE_TIME),
> +		.battery_id = cpu_to_le32(0),
> +		.rate = cpu_to_le32(0),
> +	};
> +
> +	return qcom_battmgr_request(battmgr, &request, sizeof(request));
> +}
> +
> +static const u8 sm8350_bat_prop_map[] = {
> +	[POWER_SUPPLY_PROP_STATUS] = BATT_STATUS,
> +	[POWER_SUPPLY_PROP_HEALTH] = BATT_HEALTH,
> +	[POWER_SUPPLY_PROP_PRESENT] = BATT_PRESENT,
> +	[POWER_SUPPLY_PROP_CHARGE_TYPE] = BATT_CHG_TYPE,
> +	[POWER_SUPPLY_PROP_CAPACITY] = BATT_CAPACITY,
> +	[POWER_SUPPLY_PROP_VOLTAGE_OCV] = BATT_VOLT_OCV,
> +	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = BATT_VOLT_NOW,
> +	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = BATT_VOLT_MAX,
> +	[POWER_SUPPLY_PROP_CURRENT_NOW] = BATT_CURR_NOW,
> +	[POWER_SUPPLY_PROP_TEMP] = BATT_TEMP,
> +	[POWER_SUPPLY_PROP_TECHNOLOGY] = BATT_TECHNOLOGY,
> +	[POWER_SUPPLY_PROP_CHARGE_COUNTER] =  BATT_CHG_COUNTER,
> +	[POWER_SUPPLY_PROP_CYCLE_COUNT] = BATT_CYCLE_COUNT,
> +	[POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN] =  BATT_CHG_FULL_DESIGN,
> +	[POWER_SUPPLY_PROP_CHARGE_FULL] = BATT_CHG_FULL,
> +	[POWER_SUPPLY_PROP_MODEL_NAME] = BATT_MODEL_NAME,
> +	[POWER_SUPPLY_PROP_TIME_TO_FULL_AVG] = BATT_TTF_AVG,
> +	[POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG] = BATT_TTE_AVG,
> +	[POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW,
> +};
> +
> +static int qcom_battmgr_bat_sm8350_update(struct qcom_battmgr *battmgr,
> +					  enum power_supply_property psp)
> +{
> +	unsigned int prop;
> +	int ret;
> +
> +	if (psp >= ARRAY_SIZE(sm8350_bat_prop_map))
> +		return -EINVAL;
> +
> +	prop = sm8350_bat_prop_map[psp];
> +
> +	mutex_lock(&battmgr->lock);
> +	ret = qcom_battmgr_request_property(battmgr, BATTMGR_BAT_PROPERTY_GET, prop, 0);
> +	mutex_unlock(&battmgr->lock);
> +
> +	return ret;
> +}
> +
> +static int qcom_battmgr_bat_sc8280xp_update(struct qcom_battmgr *battmgr,
> +					    enum power_supply_property psp)
> +{
> +	int ret;
> +
> +	mutex_lock(&battmgr->lock);
> +
> +	if (!battmgr->info.valid) {
> +		ret = qcom_battmgr_update_info(battmgr);
> +		if (ret < 0)
> +			goto out_unlock;
> +		battmgr->info.valid = true;
> +	}
> +
> +	ret = qcom_battmgr_update_status(battmgr);
> +	if (ret < 0)
> +		goto out_unlock;
> +
> +	if (psp == POWER_SUPPLY_PROP_TIME_TO_FULL_AVG) {
> +		ret = qcom_battmgr_update_charge_time(battmgr);
> +		if (ret < 0) {
> +			ret = -ENODATA;
> +			goto out_unlock;
> +		}
> +	}
> +
> +	if (psp == POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG) {
> +		ret = qcom_battmgr_update_discharge_time(battmgr);
> +		if (ret < 0) {
> +			ret = -ENODATA;
> +			goto out_unlock;
> +		}
> +	}
> +
> +out_unlock:
> +	mutex_unlock(&battmgr->lock);
> +	return ret;
> +}
> +
> +static int qcom_battmgr_bat_get_property(struct power_supply *psy,
> +					 enum power_supply_property psp,
> +					 union power_supply_propval *val)
> +{
> +	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
> +	enum qcom_battmgr_unit unit = battmgr->unit;
> +	int ret;
> +
> +	if (!battmgr->service_up)
> +		return -ENODEV;
> +
> +	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
> +		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
> +	else
> +		ret = qcom_battmgr_bat_sm8350_update(battmgr, psp);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_STATUS:
> +		val->intval = battmgr->status.status;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_TYPE:
> +		val->intval = battmgr->info.charge_type;
> +		break;
> +	case POWER_SUPPLY_PROP_HEALTH:
> +		val->intval = battmgr->status.health;
> +		break;
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		val->intval = battmgr->info.present;
> +		break;

This is set here...


> +	case POWER_SUPPLY_PROP_TECHNOLOGY:
> +		val->intval = battmgr->info.technology;
> +		break;
> +	case POWER_SUPPLY_PROP_CYCLE_COUNT:
> +		val->intval = battmgr->info.cycle_count;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> +		val->intval = battmgr->info.voltage_max_design;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
> +		val->intval = battmgr->info.voltage_max;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		val->intval = battmgr->status.voltage_now;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
> +		val->intval = battmgr->status.voltage_ocv;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_NOW:
> +		val->intval = battmgr->status.current_now;
> +		break;
> +	case POWER_SUPPLY_PROP_POWER_NOW:
> +		val->intval = battmgr->status.power_now;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
> +		if (unit != QCOM_BATTMGR_UNIT_mAh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.design_capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_FULL:
> +		if (unit != QCOM_BATTMGR_UNIT_mAh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.last_full_capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_EMPTY:
> +		if (unit != QCOM_BATTMGR_UNIT_mAh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.capacity_low;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_NOW:
> +		if (unit != QCOM_BATTMGR_UNIT_mAh)
> +			return -ENODATA;
> +		val->intval = battmgr->status.capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
> +		val->intval = battmgr->info.charge_count;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
> +		if (unit != QCOM_BATTMGR_UNIT_mWh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.design_capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_FULL:
> +		if (unit != QCOM_BATTMGR_UNIT_mWh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.last_full_capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
> +		if (unit != QCOM_BATTMGR_UNIT_mWh)
> +			return -ENODATA;
> +		val->intval = battmgr->info.capacity_low;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_NOW:
> +		if (unit != QCOM_BATTMGR_UNIT_mWh)
> +			return -ENODATA;
> +		val->intval = battmgr->status.capacity;
> +		break;
> +	case POWER_SUPPLY_PROP_CAPACITY:
> +		val->intval = battmgr->status.percent;
> +		break;
> +	case POWER_SUPPLY_PROP_TEMP:
> +		val->intval = battmgr->status.temperature;
> +		break;
> +	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
> +		val->intval = battmgr->status.discharge_time;
> +		break;
> +	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
> +		val->intval = battmgr->status.charge_time;
> +		break;
> +	case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
> +		val->intval = battmgr->info.year;
> +		break;
> +	case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
> +		val->intval = battmgr->info.month;
> +		break;
> +	case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
> +		val->intval = battmgr->info.day;
> +		break;
> +	case POWER_SUPPLY_PROP_MODEL_NAME:
> +		val->strval = battmgr->info.model_number;
> +		break;
> +	case POWER_SUPPLY_PROP_MANUFACTURER:
> +		val->strval = battmgr->info.oem_info;
> +		break;
> +	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
> +		val->strval = battmgr->info.serial_number;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const enum power_supply_property sc8280xp_bat_props[] = {
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_TECHNOLOGY,
> +	POWER_SUPPLY_PROP_CYCLE_COUNT,
> +	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_POWER_NOW,
> +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
> +	POWER_SUPPLY_PROP_CHARGE_FULL,
> +	POWER_SUPPLY_PROP_CHARGE_EMPTY,
> +	POWER_SUPPLY_PROP_CHARGE_NOW,
> +	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
> +	POWER_SUPPLY_PROP_ENERGY_FULL,
> +	POWER_SUPPLY_PROP_ENERGY_EMPTY,
> +	POWER_SUPPLY_PROP_ENERGY_NOW,
> +	POWER_SUPPLY_PROP_TEMP,
> +	POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
> +	POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
> +	POWER_SUPPLY_PROP_MANUFACTURE_DAY,
> +	POWER_SUPPLY_PROP_MODEL_NAME,
> +	POWER_SUPPLY_PROP_MANUFACTURER,
> +	POWER_SUPPLY_PROP_SERIAL_NUMBER,
> +};

But never added above or in sm8350_bat_props, so the "present" file in 
sysfs never shows up.

This came up on IRC, due to new upower 1.90 release and 
https://gitlab.com/freedesktop.org/upower/upower/-/issues/208

> +
> +static const struct power_supply_desc sc8280xp_bat_psy_desc = {
> +	.name = "qcom-battmgr-bat",
> +	.type = POWER_SUPPLY_TYPE_BATTERY,
> +	.properties = sc8280xp_bat_props,
> +	.num_properties = ARRAY_SIZE(sc8280xp_bat_props),
> +	.get_property = qcom_battmgr_bat_get_property,
> +};
> +
> +static const enum power_supply_property sm8350_bat_props[] = {
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_HEALTH,
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_CHARGE_TYPE,
> +	POWER_SUPPLY_PROP_CAPACITY,
> +	POWER_SUPPLY_PROP_VOLTAGE_OCV,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_VOLTAGE_MAX,
> +	POWER_SUPPLY_PROP_CURRENT_NOW,
> +	POWER_SUPPLY_PROP_TEMP,
> +	POWER_SUPPLY_PROP_TECHNOLOGY,
> +	POWER_SUPPLY_PROP_CHARGE_COUNTER,
> +	POWER_SUPPLY_PROP_CYCLE_COUNT,
> +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
> +	POWER_SUPPLY_PROP_CHARGE_FULL,
> +	POWER_SUPPLY_PROP_MODEL_NAME,
> +	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
> +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
> +	POWER_SUPPLY_PROP_POWER_NOW,
> +};
> +
> +static const struct power_supply_desc sm8350_bat_psy_desc = {
> +	.name = "qcom-battmgr-bat",
> +	.type = POWER_SUPPLY_TYPE_BATTERY,
> +	.properties = sm8350_bat_props,
> +	.num_properties = ARRAY_SIZE(sm8350_bat_props),
> +	.get_property = qcom_battmgr_bat_get_property,
> +};
> +
> +static int qcom_battmgr_ac_get_property(struct power_supply *psy,
> +					enum power_supply_property psp,
> +					union power_supply_propval *val)
> +{
> +	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
> +	int ret;
> +
> +	if (!battmgr->service_up)
> +		return -ENODEV;
> +
> +	ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
> +	if (ret)
> +		return ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		val->intval = battmgr->ac.online;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const enum power_supply_property sc8280xp_ac_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +};
> +
> +static const struct power_supply_desc sc8280xp_ac_psy_desc = {
> +	.name = "qcom-battmgr-ac",
> +	.type = POWER_SUPPLY_TYPE_MAINS,
> +	.properties = sc8280xp_ac_props,
> +	.num_properties = ARRAY_SIZE(sc8280xp_ac_props),
> +	.get_property = qcom_battmgr_ac_get_property,
> +};
> +
> +static const u8 sm8350_usb_prop_map[] = {
> +	[POWER_SUPPLY_PROP_ONLINE] = USB_ONLINE,
> +	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = USB_VOLT_NOW,
> +	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = USB_VOLT_MAX,
> +	[POWER_SUPPLY_PROP_CURRENT_NOW] = USB_CURR_NOW,
> +	[POWER_SUPPLY_PROP_CURRENT_MAX] = USB_CURR_MAX,
> +	[POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT] = USB_INPUT_CURR_LIMIT,
> +	[POWER_SUPPLY_PROP_USB_TYPE] = USB_TYPE,
> +};
> +
> +static int qcom_battmgr_usb_sm8350_update(struct qcom_battmgr *battmgr,
> +					  enum power_supply_property psp)
> +{
> +	unsigned int prop;
> +	int ret;
> +
> +	if (psp >= ARRAY_SIZE(sm8350_usb_prop_map))
> +		return -EINVAL;
> +
> +	prop = sm8350_usb_prop_map[psp];
> +
> +	mutex_lock(&battmgr->lock);
> +	ret = qcom_battmgr_request_property(battmgr, BATTMGR_USB_PROPERTY_GET, prop, 0);
> +	mutex_unlock(&battmgr->lock);
> +
> +	return ret;
> +}
> +
> +static int qcom_battmgr_usb_get_property(struct power_supply *psy,
> +					 enum power_supply_property psp,
> +					 union power_supply_propval *val)
> +{
> +	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
> +	int ret;
> +
> +	if (!battmgr->service_up)
> +		return -ENODEV;
> +
> +	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
> +		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
> +	else
> +		ret = qcom_battmgr_usb_sm8350_update(battmgr, psp);
> +	if (ret)
> +		return ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		val->intval = battmgr->usb.online;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		val->intval = battmgr->usb.voltage_now;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
> +		val->intval = battmgr->usb.voltage_max;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_NOW:
> +		val->intval = battmgr->usb.current_now;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_MAX:
> +		val->intval = battmgr->usb.current_max;
> +		break;
> +	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> +		val->intval = battmgr->usb.current_limit;
> +		break;
> +	case POWER_SUPPLY_PROP_USB_TYPE:
> +		val->intval = battmgr->usb.usb_type;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const enum power_supply_usb_type usb_psy_supported_types[] = {
> +	POWER_SUPPLY_USB_TYPE_UNKNOWN,
> +	POWER_SUPPLY_USB_TYPE_SDP,
> +	POWER_SUPPLY_USB_TYPE_DCP,
> +	POWER_SUPPLY_USB_TYPE_CDP,
> +	POWER_SUPPLY_USB_TYPE_ACA,
> +	POWER_SUPPLY_USB_TYPE_C,
> +	POWER_SUPPLY_USB_TYPE_PD,
> +	POWER_SUPPLY_USB_TYPE_PD_DRP,
> +	POWER_SUPPLY_USB_TYPE_PD_PPS,
> +	POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID,
> +};
> +
> +static const enum power_supply_property sc8280xp_usb_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +};
> +
> +static const struct power_supply_desc sc8280xp_usb_psy_desc = {
> +	.name = "qcom-battmgr-usb",
> +	.type = POWER_SUPPLY_TYPE_USB,
> +	.properties = sc8280xp_usb_props,
> +	.num_properties = ARRAY_SIZE(sc8280xp_usb_props),
> +	.get_property = qcom_battmgr_usb_get_property,
> +	.usb_types = usb_psy_supported_types,
> +	.num_usb_types = ARRAY_SIZE(usb_psy_supported_types),
> +};
> +
> +static const enum power_supply_property sm8350_usb_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_VOLTAGE_MAX,
> +	POWER_SUPPLY_PROP_CURRENT_NOW,
> +	POWER_SUPPLY_PROP_CURRENT_MAX,
> +	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> +	POWER_SUPPLY_PROP_USB_TYPE,
> +};
> +
> +static const struct power_supply_desc sm8350_usb_psy_desc = {
> +	.name = "qcom-battmgr-usb",
> +	.type = POWER_SUPPLY_TYPE_USB,
> +	.properties = sm8350_usb_props,
> +	.num_properties = ARRAY_SIZE(sm8350_usb_props),
> +	.get_property = qcom_battmgr_usb_get_property,
> +	.usb_types = usb_psy_supported_types,
> +	.num_usb_types = ARRAY_SIZE(usb_psy_supported_types),
> +};
> +
> +static const u8 sm8350_wls_prop_map[] = {
> +	[POWER_SUPPLY_PROP_ONLINE] = WLS_ONLINE,
> +	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = WLS_VOLT_NOW,
> +	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = WLS_VOLT_MAX,
> +	[POWER_SUPPLY_PROP_CURRENT_NOW] = WLS_CURR_NOW,
> +	[POWER_SUPPLY_PROP_CURRENT_MAX] = WLS_CURR_MAX,
> +};
> +
> +static int qcom_battmgr_wls_sm8350_update(struct qcom_battmgr *battmgr,
> +					  enum power_supply_property psp)
> +{
> +	unsigned int prop;
> +	int ret;
> +
> +	if (psp >= ARRAY_SIZE(sm8350_wls_prop_map))
> +		return -EINVAL;
> +
> +	prop = sm8350_wls_prop_map[psp];
> +
> +	mutex_lock(&battmgr->lock);
> +	ret = qcom_battmgr_request_property(battmgr, BATTMGR_WLS_PROPERTY_GET, prop, 0);
> +	mutex_unlock(&battmgr->lock);
> +
> +	return ret;
> +}
> +
> +static int qcom_battmgr_wls_get_property(struct power_supply *psy,
> +					 enum power_supply_property psp,
> +					 union power_supply_propval *val)
> +{
> +	struct qcom_battmgr *battmgr = power_supply_get_drvdata(psy);
> +	int ret;
> +
> +	if (!battmgr->service_up)
> +		return -ENODEV;
> +
> +	if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
> +		ret = qcom_battmgr_bat_sc8280xp_update(battmgr, psp);
> +	else
> +		ret = qcom_battmgr_wls_sm8350_update(battmgr, psp);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		val->intval = battmgr->wireless.online;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		val->intval = battmgr->wireless.voltage_now;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
> +		val->intval = battmgr->wireless.voltage_max;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_NOW:
> +		val->intval = battmgr->wireless.current_now;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_MAX:
> +		val->intval = battmgr->wireless.current_max;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const enum power_supply_property sc8280xp_wls_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +};
> +
> +static const struct power_supply_desc sc8280xp_wls_psy_desc = {
> +	.name = "qcom-battmgr-wls",
> +	.type = POWER_SUPPLY_TYPE_WIRELESS,
> +	.properties = sc8280xp_wls_props,
> +	.num_properties = ARRAY_SIZE(sc8280xp_wls_props),
> +	.get_property = qcom_battmgr_wls_get_property,
> +};
> +
> +static const enum power_supply_property sm8350_wls_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_VOLTAGE_MAX,
> +	POWER_SUPPLY_PROP_CURRENT_NOW,
> +	POWER_SUPPLY_PROP_CURRENT_MAX,
> +};
> +
> +static const struct power_supply_desc sm8350_wls_psy_desc = {
> +	.name = "qcom-battmgr-wls",
> +	.type = POWER_SUPPLY_TYPE_WIRELESS,
> +	.properties = sm8350_wls_props,
> +	.num_properties = ARRAY_SIZE(sm8350_wls_props),
> +	.get_property = qcom_battmgr_wls_get_property,
> +};
> +
> +static void qcom_battmgr_notification(struct qcom_battmgr *battmgr,
> +				      const struct qcom_battmgr_message *msg,
> +				      int len)
> +{
> +	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
> +	unsigned int notification;
> +
> +	if (payload_len != sizeof(msg->notification)) {
> +		dev_warn(battmgr->dev, "ignoring notification with invalid length\n");
> +		return;
> +	}
> +
> +	notification = le32_to_cpu(msg->notification);
> +	switch (notification) {
> +	case NOTIF_BAT_INFO:
> +		battmgr->info.valid = false;
> +		fallthrough;
> +	case NOTIF_BAT_PROPERTY:
> +		power_supply_changed(battmgr->bat_psy);
> +		break;
> +	case NOTIF_USB_PROPERTY:
> +		power_supply_changed(battmgr->usb_psy);
> +		break;
> +	case NOTIF_WLS_PROPERTY:
> +		power_supply_changed(battmgr->wls_psy);
> +		break;
> +	default:
> +		dev_err(battmgr->dev, "unknown notification: %#x\n", notification);
> +		break;
> +	}
> +}
> +
> +static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src)
> +{
> +	size_t len = src[0];
> +
> +	/* Some firmware versions return Pascal-style strings */
> +	if (len < BATTMGR_STRING_LEN && len == strnlen(src + 1, BATTMGR_STRING_LEN - 1)) {
> +		memcpy(dest, src + 1, len);
> +		dest[len] = '\0';
> +	} else {
> +		memcpy(dest, src, BATTMGR_STRING_LEN);
> +	}
> +}
> +
> +static unsigned int qcom_battmgr_sc8280xp_parse_technology(const char *chemistry)
> +{
> +	if (!strncmp(chemistry, "LIO", BATTMGR_CHEMISTRY_LEN))
> +		return POWER_SUPPLY_TECHNOLOGY_LION;
> +
> +	pr_err("Unknown battery technology '%s'\n", chemistry);
> +	return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
> +}
> +
> +static unsigned int qcom_battmgr_sc8280xp_convert_temp(unsigned int temperature)
> +{
> +	return DIV_ROUND_CLOSEST(temperature, 10);
> +}
> +
> +static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
> +					   const struct qcom_battmgr_message *resp,
> +					   size_t len)
> +{
> +	unsigned int opcode = le32_to_cpu(resp->hdr.opcode);
> +	unsigned int source;
> +	unsigned int state;
> +	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
> +
> +	if (payload_len < sizeof(__le32)) {
> +		dev_warn(battmgr->dev, "invalid payload length for %#x: %zd\n",
> +			 opcode, len);
> +		return;
> +	}
> +
> +	switch (opcode) {
> +	case BATTMGR_REQUEST_NOTIFICATION:
> +		battmgr->error = 0;
> +		break;
> +	case BATTMGR_BAT_INFO:
> +		if (payload_len != sizeof(resp->info)) {
> +			dev_warn(battmgr->dev,
> +				 "invalid payload length for battery information request: %zd\n",
> +				 payload_len);
> +			battmgr->error = -ENODATA;
> +			return;
> +		}
> +
> +		battmgr->unit = le32_to_cpu(resp->info.power_unit);
> +
> +		battmgr->info.design_capacity = le32_to_cpu(resp->info.design_capacity) * 1000;
> +		battmgr->info.last_full_capacity = le32_to_cpu(resp->info.last_full_capacity) * 1000;
> +		battmgr->info.voltage_max_design = le32_to_cpu(resp->info.design_voltage) * 1000;
> +		battmgr->info.capacity_low = le32_to_cpu(resp->info.capacity_low) * 1000;
> +		battmgr->info.cycle_count = le32_to_cpu(resp->info.cycle_count);
> +		qcom_battmgr_sc8280xp_strcpy(battmgr->info.model_number, resp->info.model_number);
> +		qcom_battmgr_sc8280xp_strcpy(battmgr->info.serial_number, resp->info.serial_number);
> +		battmgr->info.technology = qcom_battmgr_sc8280xp_parse_technology(resp->info.battery_chemistry);
> +		qcom_battmgr_sc8280xp_strcpy(battmgr->info.oem_info, resp->info.oem_info);
> +		battmgr->info.day = resp->info.day;
> +		battmgr->info.month = resp->info.month;
> +		battmgr->info.year = le16_to_cpu(resp->info.year);
> +		break;
> +	case BATTMGR_BAT_STATUS:
> +		if (payload_len != sizeof(resp->status)) {
> +			dev_warn(battmgr->dev,
> +				 "invalid payload length for battery status request: %zd\n",
> +				 payload_len);
> +			battmgr->error = -ENODATA;
> +			return;
> +		}
> +
> +		state = le32_to_cpu(resp->status.battery_state);
> +		if (state & BIT(0))
> +			battmgr->status.status = POWER_SUPPLY_STATUS_DISCHARGING;
> +		else if (state & BIT(1))
> +			battmgr->status.status = POWER_SUPPLY_STATUS_CHARGING;
> +		else
> +			battmgr->status.status = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +
> +		battmgr->status.capacity = le32_to_cpu(resp->status.capacity) * 1000;
> +		battmgr->status.power_now = le32_to_cpu(resp->status.rate) * 1000;
> +		battmgr->status.voltage_now = le32_to_cpu(resp->status.battery_voltage) * 1000;
> +		battmgr->status.temperature = qcom_battmgr_sc8280xp_convert_temp(le32_to_cpu(resp->status.temperature));
> +
> +		source = le32_to_cpu(resp->status.charging_source);
> +		battmgr->ac.online = source == BATTMGR_CHARGING_SOURCE_AC;
> +		battmgr->usb.online = source == BATTMGR_CHARGING_SOURCE_USB;
> +		battmgr->wireless.online = source == BATTMGR_CHARGING_SOURCE_WIRELESS;
> +		break;
> +	case BATTMGR_BAT_DISCHARGE_TIME:
> +		battmgr->status.discharge_time = le32_to_cpu(resp->time);
> +		break;
> +	case BATTMGR_BAT_CHARGE_TIME:
> +		battmgr->status.charge_time = le32_to_cpu(resp->time);
> +		break;
> +	default:
> +		dev_warn(battmgr->dev, "unknown message %#x\n", opcode);
> +		break;
> +	}
> +
> +	complete(&battmgr->ack);
> +}
> +
> +static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
> +					 const struct qcom_battmgr_message *resp,
> +					 size_t len)
> +{
> +	unsigned int property;
> +	unsigned int opcode = le32_to_cpu(resp->hdr.opcode);
> +	size_t payload_len = len - sizeof(struct pmic_glink_hdr);
> +	unsigned int val;
> +
> +	if (payload_len < sizeof(__le32)) {
> +		dev_warn(battmgr->dev, "invalid payload length for %#x: %zd\n",
> +			 opcode, len);
> +		return;
> +	}
> +
> +	switch (opcode) {
> +	case BATTMGR_BAT_PROPERTY_GET:
> +		property = le32_to_cpu(resp->intval.property);
> +		if (property == BATT_MODEL_NAME) {
> +			if (payload_len != sizeof(resp->strval)) {
> +				dev_warn(battmgr->dev,
> +					 "invalid payload length for BATT_MODEL_NAME request: %zd\n",
> +					 payload_len);
> +				battmgr->error = -ENODATA;
> +				return;
> +			}
> +		} else {
> +			if (payload_len != sizeof(resp->intval)) {
> +				dev_warn(battmgr->dev,
> +					 "invalid payload length for %#x request: %zd\n",
> +					 property, payload_len);
> +				battmgr->error = -ENODATA;
> +				return;
> +			}
> +
> +			battmgr->error = le32_to_cpu(resp->intval.result);
> +			if (battmgr->error)
> +				goto out_complete;
> +		}
> +
> +		switch (property) {
> +		case BATT_STATUS:
> +			battmgr->status.status = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_HEALTH:
> +			battmgr->status.health = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_PRESENT:
> +			battmgr->info.present = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CHG_TYPE:
> +			battmgr->info.charge_type = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CAPACITY:
> +			battmgr->status.percent = le32_to_cpu(resp->intval.value);
> +			do_div(battmgr->status.percent, 100);
> +			break;
> +		case BATT_VOLT_OCV:
> +			battmgr->status.voltage_ocv = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_VOLT_NOW:
> +			battmgr->status.voltage_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_VOLT_MAX:
> +			battmgr->info.voltage_max = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CURR_NOW:
> +			battmgr->status.current_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_TEMP:
> +			val = le32_to_cpu(resp->intval.value);
> +			battmgr->status.temperature = DIV_ROUND_CLOSEST(val, 10);
> +			break;
> +		case BATT_TECHNOLOGY:
> +			battmgr->info.technology = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CHG_COUNTER:
> +			battmgr->info.charge_count = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CYCLE_COUNT:
> +			battmgr->info.cycle_count = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CHG_FULL_DESIGN:
> +			battmgr->info.design_capacity = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_CHG_FULL:
> +			battmgr->info.last_full_capacity = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_MODEL_NAME:
> +			strscpy(battmgr->info.model_number, resp->strval.model, BATTMGR_STRING_LEN);
> +			break;
> +		case BATT_TTF_AVG:
> +			battmgr->status.charge_time = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_TTE_AVG:
> +			battmgr->status.discharge_time = le32_to_cpu(resp->intval.value);
> +			break;
> +		case BATT_POWER_NOW:
> +			battmgr->status.power_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		default:
> +			dev_warn(battmgr->dev, "unknown property %#x\n", property);
> +			break;
> +		}
> +		break;
> +	case BATTMGR_USB_PROPERTY_GET:
> +		property = le32_to_cpu(resp->intval.property);
> +		if (payload_len != sizeof(resp->intval)) {
> +			dev_warn(battmgr->dev,
> +				 "invalid payload length for %#x request: %zd\n",
> +				 property, payload_len);
> +			battmgr->error = -ENODATA;
> +			return;
> +		}
> +
> +		battmgr->error = le32_to_cpu(resp->intval.result);
> +		if (battmgr->error)
> +			goto out_complete;
> +
> +		switch (property) {
> +		case USB_ONLINE:
> +			battmgr->usb.online = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_VOLT_NOW:
> +			battmgr->usb.voltage_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_VOLT_MAX:
> +			battmgr->usb.voltage_max = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_CURR_NOW:
> +			battmgr->usb.current_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_CURR_MAX:
> +			battmgr->usb.current_max = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_INPUT_CURR_LIMIT:
> +			battmgr->usb.current_limit = le32_to_cpu(resp->intval.value);
> +			break;
> +		case USB_TYPE:
> +			battmgr->usb.usb_type = le32_to_cpu(resp->intval.value);
> +			break;
> +		default:
> +			dev_warn(battmgr->dev, "unknown property %#x\n", property);
> +			break;
> +		}
> +		break;
> +	case BATTMGR_WLS_PROPERTY_GET:
> +		property = le32_to_cpu(resp->intval.property);
> +		if (payload_len != sizeof(resp->intval)) {
> +			dev_warn(battmgr->dev,
> +				 "invalid payload length for %#x request: %zd\n",
> +				 property, payload_len);
> +			battmgr->error = -ENODATA;
> +			return;
> +		}
> +
> +		battmgr->error = le32_to_cpu(resp->intval.result);
> +		if (battmgr->error)
> +			goto out_complete;
> +
> +		switch (property) {
> +		case WLS_ONLINE:
> +			battmgr->wireless.online = le32_to_cpu(resp->intval.value);
> +			break;
> +		case WLS_VOLT_NOW:
> +			battmgr->wireless.voltage_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case WLS_VOLT_MAX:
> +			battmgr->wireless.voltage_max = le32_to_cpu(resp->intval.value);
> +			break;
> +		case WLS_CURR_NOW:
> +			battmgr->wireless.current_now = le32_to_cpu(resp->intval.value);
> +			break;
> +		case WLS_CURR_MAX:
> +			battmgr->wireless.current_max = le32_to_cpu(resp->intval.value);
> +			break;
> +		default:
> +			dev_warn(battmgr->dev, "unknown property %#x\n", property);
> +			break;
> +		}
> +		break;
> +	case BATTMGR_REQUEST_NOTIFICATION:
> +		battmgr->error = 0;
> +		break;
> +	default:
> +		dev_warn(battmgr->dev, "unknown message %#x\n", opcode);
> +		break;
> +	}
> +
> +out_complete:
> +	complete(&battmgr->ack);
> +}
> +
> +static void qcom_battmgr_callback(const void *data, size_t len, void *priv)
> +{
> +	const struct pmic_glink_hdr *hdr = data;
> +	struct qcom_battmgr *battmgr = priv;
> +	unsigned int opcode = le32_to_cpu(hdr->opcode);
> +
> +	if (opcode == BATTMGR_NOTIFICATION)
> +		qcom_battmgr_notification(battmgr, data, len);
> +	else if (battmgr->variant == QCOM_BATTMGR_SC8280XP)
> +		qcom_battmgr_sc8280xp_callback(battmgr, data, len);
> +	else
> +		qcom_battmgr_sm8350_callback(battmgr, data, len);
> +}
> +
> +static void qcom_battmgr_enable_worker(struct work_struct *work)
> +{
> +	struct qcom_battmgr *battmgr = container_of(work, struct qcom_battmgr, enable_work);
> +	struct qcom_battmgr_enable_request req = {
> +		.hdr.owner = PMIC_GLINK_OWNER_BATTMGR,
> +		.hdr.type = PMIC_GLINK_NOTIFY,
> +		.hdr.opcode = BATTMGR_REQUEST_NOTIFICATION,
> +	};
> +	int ret;
> +
> +	ret = qcom_battmgr_request(battmgr, &req, sizeof(req));
> +	if (ret)
> +		dev_err(battmgr->dev, "failed to request power notifications\n");
> +}
> +
> +static void qcom_battmgr_pdr_notify(void *priv, int state)
> +{
> +	struct qcom_battmgr *battmgr = priv;
> +
> +	if (state == SERVREG_SERVICE_STATE_UP) {
> +		battmgr->service_up = true;
> +		schedule_work(&battmgr->enable_work);
> +	} else {
> +		battmgr->service_up = false;
> +	}
> +}
> +
> +static const struct of_device_id qcom_battmgr_of_variants[] = {
> +	{ .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
> +	{ .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)QCOM_BATTMGR_SC8280XP },
> +	/* Unmatched devices falls back to QCOM_BATTMGR_SM8350 */
> +	{}
> +};
> +
> +static char *qcom_battmgr_battery[] = { "battery" };
> +
> +static int qcom_battmgr_probe(struct auxiliary_device *adev,
> +			      const struct auxiliary_device_id *id)
> +{
> +	struct power_supply_config psy_cfg_supply = {};
> +	struct power_supply_config psy_cfg = {};
> +	const struct of_device_id *match;
> +	struct qcom_battmgr *battmgr;
> +	struct device *dev = &adev->dev;
> +
> +	battmgr = devm_kzalloc(dev, sizeof(*battmgr), GFP_KERNEL);
> +	if (!battmgr)
> +		return -ENOMEM;
> +
> +	battmgr->dev = dev;
> +
> +	psy_cfg.drv_data = battmgr;
> +	psy_cfg.of_node = adev->dev.of_node;
> +
> +	psy_cfg_supply.drv_data = battmgr;
> +	psy_cfg_supply.of_node = adev->dev.of_node;
> +	psy_cfg_supply.supplied_to = qcom_battmgr_battery;
> +	psy_cfg_supply.num_supplicants = 1;
> +
> +	INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker);
> +	mutex_init(&battmgr->lock);
> +	init_completion(&battmgr->ack);
> +
> +	match = of_match_device(qcom_battmgr_of_variants, dev->parent);
> +	if (match)
> +		battmgr->variant = (unsigned long)match->data;
> +	else
> +		battmgr->variant = QCOM_BATTMGR_SM8350;
> +
> +	battmgr->client = devm_pmic_glink_register_client(dev,
> +							  PMIC_GLINK_OWNER_BATTMGR,
> +							  qcom_battmgr_callback,
> +							  qcom_battmgr_pdr_notify,
> +							  battmgr);
> +	if (IS_ERR(battmgr->client))
> +		return PTR_ERR(battmgr->client);
> +
> +	if (battmgr->variant == QCOM_BATTMGR_SC8280XP) {
> +		battmgr->bat_psy = devm_power_supply_register(dev, &sc8280xp_bat_psy_desc, &psy_cfg);
> +		if (IS_ERR(battmgr->bat_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
> +					     "failed to register battery power supply\n");
> +
> +		battmgr->ac_psy = devm_power_supply_register(dev, &sc8280xp_ac_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->ac_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->ac_psy),
> +					     "failed to register AC power supply\n");
> +
> +		battmgr->usb_psy = devm_power_supply_register(dev, &sc8280xp_usb_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->usb_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
> +					     "failed to register USB power supply\n");
> +
> +		battmgr->wls_psy = devm_power_supply_register(dev, &sc8280xp_wls_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->wls_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
> +					     "failed to register wireless charing power supply\n");
> +	} else {
> +		battmgr->bat_psy = devm_power_supply_register(dev, &sm8350_bat_psy_desc, &psy_cfg);
> +		if (IS_ERR(battmgr->bat_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->bat_psy),
> +					     "failed to register battery power supply\n");
> +
> +		battmgr->usb_psy = devm_power_supply_register(dev, &sm8350_usb_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->usb_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->usb_psy),
> +					     "failed to register USB power supply\n");
> +
> +		battmgr->wls_psy = devm_power_supply_register(dev, &sm8350_wls_psy_desc, &psy_cfg_supply);
> +		if (IS_ERR(battmgr->wls_psy))
> +			return dev_err_probe(dev, PTR_ERR(battmgr->wls_psy),
> +					     "failed to register wireless charing power supply\n");
> +	}
> +
> +	dev_set_drvdata(dev, battmgr);
> +
> +	return 0;
> +}
> +
> +static const struct auxiliary_device_id qcom_battmgr_id_table[] = {
> +	{ .name = "pmic_glink.power-supply", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(auxiliary, qcom_battmgr_id_table);
> +
> +static struct auxiliary_driver qcom_battmgr_driver = {
> +	.name = "pmic_glink_power_supply",
> +	.probe = qcom_battmgr_probe,
> +	.id_table = qcom_battmgr_id_table,
> +};
> +
> +static int __init qcom_battmgr_init(void)
> +{
> +	return auxiliary_driver_register(&qcom_battmgr_driver);
> +}
> +module_init(qcom_battmgr_init);
> +
> +static void __exit qcom_battmgr_exit(void)
> +{
> +	auxiliary_driver_unregister(&qcom_battmgr_driver);
> +}
> +module_exit(qcom_battmgr_exit);
> +
> +MODULE_DESCRIPTION("Qualcomm PMIC GLINK battery manager driver");
> +MODULE_LICENSE("GPL");

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

* Re: [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
  2022-08-18  8:05   ` Krzysztof Kozlowski
@ 2022-10-24 20:02   ` Andrew Halaney
  2022-10-26 19:52   ` Subbaraman Narayanamurthy
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2022-10-24 20:02 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm, echanude

On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote:
> The PMIC GLINK service runs on one of the co-processors of some modern
> Qualcomm platforms and implements USB-C and battery managements. It uses
> a message based protocol over GLINK for communication with the OS, hence
> the name.
> 
> The driver implemented provides the rpmsg device for communication and
> uses auxilirary bus to spawn off individual devices in respsective
> subsystem. The auxilirary devices are spawned off from a
> platform_device, so that the drm_bridge is available early, to allow the
> DisplayPort driver to probe even before the remoteproc has spun up.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
<snip>
> diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c
> new file mode 100644
> index 000000000000..d42127521eca
> --- /dev/null
> +++ b/drivers/soc/qcom/pmic_glink.c
> @@ -0,0 +1,336 @@
<snip>
> +
> +static void _devm_pmic_glink_release_client(struct device *dev, void *res)
> +{
> +	struct pmic_glink_client *client = *(struct pmic_glink_client **)res;

As Eric Chanudet pointed out to me, this should be:

	struct pmic_glink_client *client = (struct pmic_glink_client *)res;

Otherwise you get a splat like below (which somehow resulted in my
panel output not to work on my x13s... not sure of the connection
there, and is easily reproducible with a probe deferal or qcom_battmgr
unload):

        Unable to handle kernel NULL pointer dereference at virtual address 0000000000000958
        Mem abort info:
          ESR = 0x0000000096000004
          EC = 0x25: DABT (current EL), IL = 32 bits
          ESR = 0x0000000096000004
          EC = 0x25: DABT (current EL), IL = 32 bits
          SET = 0, FnV = 0
          EA = 0, S1PTW = 0
          FSC = 0x04: level 0 translation fault
        Data abort info:
          ISV = 0, ISS = 0x00000004
          CM = 0, WnR = 0
        user pgtable: 4k pages, 48-bit VAs, pgdp=0000000106b92000
        [0000000000000958] pgd=0000000000000000, p4d=0000000000000000
        Internal error: Oops: 96000004 [#1] PREEMPT SMP
        Modules linked in: llcc_qcom qcom_battmgr aes_ce_blk pmic_glink_altmode aes_ce_cipher ghash_ce gf128mul sha2_ce sha256_arm64 sha1_ce gpio_sbu_mux pmic_glink gpio_keys autofs4
        CPU: 2 PID: 182 Comm: kworker/u16:5 Not tainted 6.0.0-rc6 #29
        Hardware name: LENOVO 21BX0016US/21BX0016US, BIOS N3HET47W (1.19 ) 07/04/2022
        Workqueue: events_unbound deferred_probe_work_func
        pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
        pc : mutex_lock+0x1c/0x60
        lr : _devm_pmic_glink_release_client+0x2c/0x74 [pmic_glink]
        sp : ffff80000c553970
        x29: ffff80000c553970 x28: 0000000000000000 x27: 0000000000000000
        x26: ffffc297e181e0e8 x25: ffffc297e181d000 x24: ffffc2984efd9a80
        x23: ffffc2984ea7a008 x22: ffff1738863cc3a0 x21: ffff80000c553a28
        x20: 0000000000000958 x19: ffff1738863cc9f8 x18: ffffffffffffffff
        x17: 0000000000000000 x16: ffffc2984e1bb110 x15: 61622d6d6f63713d
        x14: ffffc2984f3b23e0 x13: 554e514553007972 x12: 0000000000000000
        x11: 00313731333d4d55 x10: 0000000000000000 x9 : ffffc297e181d1cc
        x8 : ffff80000c553910 x7 : 0000000000000000 x6 : 0000000080200016
        x5 : 0000000000000038 x4 : 0000000000000000 x3 : 0000000000000958
        x2 : ffff17388522c100 x1 : 0000000000000000 x0 : 0000000000000958
        Call trace:
         mutex_lock+0x1c/0x60
         release_nodes+0x68/0x100
         devres_release_all+0x94/0xf0
         device_unbind_cleanup+0x20/0x70
         device_release_driver_internal+0x214/0x260
         device_release_driver+0x20/0x30
         bus_remove_device+0xdc/0x170
         device_del+0x178/0x3ac
         pmic_glink_probe+0x1e8/0x240 [pmic_glink]
         platform_probe+0x70/0xcc
         really_probe+0xc8/0x3e0
         __driver_probe_device+0x84/0x190
         driver_probe_device+0x44/0x100
         __device_attach_driver+0xc4/0x160
         bus_for_each_drv+0x84/0xe0
         __device_attach+0xa4/0x1c4
         device_initial_probe+0x1c/0x30
         bus_probe_device+0xa4/0xb0
         deferred_probe_work_func+0xc0/0x114
         process_one_work+0x1ec/0x470
         worker_thread+0x74/0x410
         kthread+0xfc/0x110
         ret_from_fork+0x10/0x20
        Code: d5384102 d503201f d2800001 aa0103e4 (c8e47c02)
        ---[ end trace 0000000000000000 ]---

All credit to Eric[0] on that, I'm just tying up loose ends.

[0] https://gitlab.com/ahalaney/linux/-/commit/1819fbccd03de430d9fd4c58ded35f5be83e9aa8

Thanks,
Andrew


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

* Re: [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support
  2022-08-18  3:15 ` [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support Bjorn Andersson
@ 2022-10-25  8:24   ` Johan Hovold
  2022-10-25  8:32   ` Johan Hovold
  1 sibling, 0 replies; 18+ messages in thread
From: Johan Hovold @ 2022-10-25  8:24 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

On Wed, Aug 17, 2022 at 08:15:11PM -0700, Bjorn Andersson wrote:
> With the PMIC GLINK service, the host OS subscribes to USB-C altmode
> messages, which are sent by the firmware to notify the host OS about
> state updates and HPD interrupts.
> 
> The pmic_glink_altmode driver registers for these notifications and
> propagates the notifications as typec_mux, typec_switch and DRM OOB
> notifications as necessary to implement DisplayPort altmode support.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  drivers/soc/qcom/Makefile             |   1 +
>  drivers/soc/qcom/pmic_glink_altmode.c | 477 ++++++++++++++++++++++++++
>  2 files changed, 478 insertions(+)
>  create mode 100644 drivers/soc/qcom/pmic_glink_altmode.c

> diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
> new file mode 100644
> index 000000000000..8d2d563cb756
> --- /dev/null
> +++ b/drivers/soc/qcom/pmic_glink_altmode.c

> +static void pmic_glink_altmode_worker(struct work_struct *work)
> +{
> +	struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work);
> +	struct pmic_glink_altmode *altmode = alt_port->altmode;
> +
> +	typec_switch_set(alt_port->typec_switch, alt_port->orientation);
> +
> +	if (alt_port->svid == USB_TYPEC_DP_SID)
> +		pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode,
> +					     alt_port->hpd_state, alt_port->hpd_irq);
> +	else
> +		pmic_glink_altmode_enable_usb(altmode, alt_port);
> +
> +	if (alt_port->hpd_state)
> +		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected);
> +	else
> +		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected);
> +
> +	pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index);
> +};

I'm seeing fairly frequent crashes during boot of the X13s due to these
notifications being propagated before things have been fully set up:

[   16.591910] panel-simple-dp-aux aux-aea0000.displayport-controller: Detected SHP LQ140M1JW48 (0x1511)
[   16.592142] qcom,fastrpc-cb 1b300000.remoteproc:glink-edge:fastrpc:compute-cb@12: Adding to iommu group 17
[   16.597644] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
[   16.597653] Mem abort info:
[   16.597657]   ESR = 0x0000000096000004
[   16.597663]   EC = 0x25: DABT (current EL), IL = 32 bits
[   16.597670]   SET = 0, FnV = 0
[   16.597675]   EA = 0, S1PTW = 0
[   16.597680]   FSC = 0x04: level 0 translation fault
[   16.597686] Data abort info:
[   16.597689]   ISV = 0, ISS = 0x00000004
[   16.597694]   CM = 0, WnR = 0
[   16.597698] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000106b93000
[   16.597706] [0000000000000010] pgd=0000000000000000, p4d=0000000000000000
[   16.597722] Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
[   16.597731] Dumping ftrace buffer:
[   16.597742]    (ftrace buffer empty)
[   16.597744] Modules linked in: fastrpc(+) rpmsg_ctrl qrtr_smd rpmsg_char qcom_battmgr pmic_glink_altmode rtc_pm8xxxr
[   16.597831] CPU: 0 PID: 389 Comm: kworker/0:3 Not tainted 6.1.0-rc2 #195
[   16.597838] Hardware name: Qualcomm QRD, BIOS 6.0.220110.BOOT.MXF.1.1-00470-MAKENA-1 01/10/2022
[   16.597842] Workqueue: events pmic_glink_altmode_worker [pmic_glink_altmode]
[   16.597864] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[   16.597870] pc : drm_kms_helper_hotplug_event+0x1c/0x50
[   16.597882] lr : drm_kms_helper_hotplug_event+0x18/0x50
[   16.597887] sp : ffff80000c20bca0
[   16.597889] x29: ffff80000c20bca0 x28: ffffdba5eadbb000 x27: ffff22a9f6f2dc05
[   16.597898] x26: ffffdba5eadc0b20 x25: ffffdba5eadd8ca0 x24: 0000000000000000
[   16.597906] x23: 0000000000000003 x22: ffff22a888526000 x21: 0000000000000002
[   16.597914] x20: ffff22a88ceed000 x19: ffff22a888526000 x18: 0000000000000020
[   16.597921] x17: 4d003632323d524f x16: 4a414d00313d4755 x15: 4c50544f48006d72
[   16.597929] x14: 0000000000000001 x13: 0000000000000040 x12: 0000000000000000
[   16.597936] x11: 0000000000000000 x10: 0000000000000228 x9 : 0000000000000000
[   16.597944] x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000062e00
[   16.597951] x5 : 0000000000000000 x4 : ffff22a9f6f2d290 x3 : 0000000000062f00
[   16.597959] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000
[   16.597965] Call trace:
[   16.597968]  drm_kms_helper_hotplug_event+0x1c/0x50
[   16.597973]  drm_bridge_connector_hpd_cb+0xa0/0xc0
[   16.597983]  drm_bridge_hpd_notify+0x40/0x60
[   16.597990]  pmic_glink_altmode_worker+0xc0/0x150 [pmic_glink_altmode]
[   16.598006]  process_one_work+0x288/0x6c0
[   16.598014]  worker_thread+0x74/0x450
[   16.598019]  kthread+0x118/0x120
[   16.598028]  ret_from_fork+0x10/0x20
[   16.598039] Code: f9000bf3 aa0003f3 97ff22af f9445e60 (f9400801) 
[   16.598043] ---[ end trace 0000000000000000 ]---
[   16.603424] [drm] Initialized msm 1.9.0 20130625 for ae01000.mdp on minor 0

I've verified that it is the funcs pointer in
drm_kms_helper_hotplug_event() which is NULL and a hack like the below
prevents the crash:

diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 69b0b2b9cc1c..d515f5b6f3d5 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -661,7 +661,9 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev)
 {
        /* send a uevent + call fbdev */
        drm_sysfs_hotplug_event(dev);
-       if (dev->mode_config.funcs->output_poll_changed)
+
+       WARN_ON(!dev->mode_config.funcs);
+       if (dev->mode_config.funcs && dev->mode_config.funcs->output_poll_changed)
                dev->mode_config.funcs->output_poll_changed(dev);
 
        drm_client_dev_hotplug(dev);

It appears that pointer is set in msm_drm_init(), which suggests events
are being forwarded before the driver is ready.

Johan

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

* Re: [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support
  2022-08-18  3:15 ` [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support Bjorn Andersson
  2022-10-25  8:24   ` Johan Hovold
@ 2022-10-25  8:32   ` Johan Hovold
  1 sibling, 0 replies; 18+ messages in thread
From: Johan Hovold @ 2022-10-25  8:32 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

[ Resending to Bjorn's current address. ]

On Wed, Aug 17, 2022 at 08:15:11PM -0700, Bjorn Andersson wrote:
> With the PMIC GLINK service, the host OS subscribes to USB-C altmode
> messages, which are sent by the firmware to notify the host OS about
> state updates and HPD interrupts.
> 
> The pmic_glink_altmode driver registers for these notifications and
> propagates the notifications as typec_mux, typec_switch and DRM OOB
> notifications as necessary to implement DisplayPort altmode support.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  drivers/soc/qcom/Makefile             |   1 +
>  drivers/soc/qcom/pmic_glink_altmode.c | 477 ++++++++++++++++++++++++++
>  2 files changed, 478 insertions(+)
>  create mode 100644 drivers/soc/qcom/pmic_glink_altmode.c

> diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
> new file mode 100644
> index 000000000000..8d2d563cb756
> --- /dev/null
> +++ b/drivers/soc/qcom/pmic_glink_altmode.c

> +static void pmic_glink_altmode_worker(struct work_struct *work)
> +{
> +	struct pmic_glink_altmode_port *alt_port = work_to_altmode_port(work);
> +	struct pmic_glink_altmode *altmode = alt_port->altmode;
> +
> +	typec_switch_set(alt_port->typec_switch, alt_port->orientation);
> +
> +	if (alt_port->svid == USB_TYPEC_DP_SID)
> +		pmic_glink_altmode_enable_dp(altmode, alt_port, alt_port->mode,
> +					     alt_port->hpd_state, alt_port->hpd_irq);
> +	else
> +		pmic_glink_altmode_enable_usb(altmode, alt_port);
> +
> +	if (alt_port->hpd_state)
> +		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_connected);
> +	else
> +		drm_bridge_hpd_notify(&alt_port->bridge, connector_status_disconnected);
> +
> +	pmic_glink_altmode_request(altmode, ALTMODE_PAN_ACK, alt_port->index);
> +};

I'm seeing fairly frequent crashes during boot of the X13s due to these
notifications being propagated before things have been fully set up:

[   16.591910] panel-simple-dp-aux aux-aea0000.displayport-controller: Detected SHP LQ140M1JW48 (0x1511)
[   16.592142] qcom,fastrpc-cb 1b300000.remoteproc:glink-edge:fastrpc:compute-cb@12: Adding to iommu group 17
[   16.597644] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
[   16.597653] Mem abort info:
[   16.597657]   ESR = 0x0000000096000004
[   16.597663]   EC = 0x25: DABT (current EL), IL = 32 bits
[   16.597670]   SET = 0, FnV = 0
[   16.597675]   EA = 0, S1PTW = 0
[   16.597680]   FSC = 0x04: level 0 translation fault
[   16.597686] Data abort info:
[   16.597689]   ISV = 0, ISS = 0x00000004
[   16.597694]   CM = 0, WnR = 0
[   16.597698] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000106b93000
[   16.597706] [0000000000000010] pgd=0000000000000000, p4d=0000000000000000
[   16.597722] Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
[   16.597731] Dumping ftrace buffer:
[   16.597742]    (ftrace buffer empty)
[   16.597744] Modules linked in: fastrpc(+) rpmsg_ctrl qrtr_smd rpmsg_char qcom_battmgr pmic_glink_altmode rtc_pm8xxxr
[   16.597831] CPU: 0 PID: 389 Comm: kworker/0:3 Not tainted 6.1.0-rc2 #195
[   16.597838] Hardware name: Qualcomm QRD, BIOS 6.0.220110.BOOT.MXF.1.1-00470-MAKENA-1 01/10/2022
[   16.597842] Workqueue: events pmic_glink_altmode_worker [pmic_glink_altmode]
[   16.597864] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[   16.597870] pc : drm_kms_helper_hotplug_event+0x1c/0x50
[   16.597882] lr : drm_kms_helper_hotplug_event+0x18/0x50
[   16.597887] sp : ffff80000c20bca0
[   16.597889] x29: ffff80000c20bca0 x28: ffffdba5eadbb000 x27: ffff22a9f6f2dc05
[   16.597898] x26: ffffdba5eadc0b20 x25: ffffdba5eadd8ca0 x24: 0000000000000000
[   16.597906] x23: 0000000000000003 x22: ffff22a888526000 x21: 0000000000000002
[   16.597914] x20: ffff22a88ceed000 x19: ffff22a888526000 x18: 0000000000000020
[   16.597921] x17: 4d003632323d524f x16: 4a414d00313d4755 x15: 4c50544f48006d72
[   16.597929] x14: 0000000000000001 x13: 0000000000000040 x12: 0000000000000000
[   16.597936] x11: 0000000000000000 x10: 0000000000000228 x9 : 0000000000000000
[   16.597944] x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000062e00
[   16.597951] x5 : 0000000000000000 x4 : ffff22a9f6f2d290 x3 : 0000000000062f00
[   16.597959] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000
[   16.597965] Call trace:
[   16.597968]  drm_kms_helper_hotplug_event+0x1c/0x50
[   16.597973]  drm_bridge_connector_hpd_cb+0xa0/0xc0
[   16.597983]  drm_bridge_hpd_notify+0x40/0x60
[   16.597990]  pmic_glink_altmode_worker+0xc0/0x150 [pmic_glink_altmode]
[   16.598006]  process_one_work+0x288/0x6c0
[   16.598014]  worker_thread+0x74/0x450
[   16.598019]  kthread+0x118/0x120
[   16.598028]  ret_from_fork+0x10/0x20
[   16.598039] Code: f9000bf3 aa0003f3 97ff22af f9445e60 (f9400801) 
[   16.598043] ---[ end trace 0000000000000000 ]---
[   16.603424] [drm] Initialized msm 1.9.0 20130625 for ae01000.mdp on minor 0

I've verified that it is the funcs pointer in
drm_kms_helper_hotplug_event() which is NULL and a hack like the below
prevents the crash:

diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 69b0b2b9cc1c..d515f5b6f3d5 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -661,7 +661,9 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev)
 {
        /* send a uevent + call fbdev */
        drm_sysfs_hotplug_event(dev);
-       if (dev->mode_config.funcs->output_poll_changed)
+
+       WARN_ON(!dev->mode_config.funcs);
+       if (dev->mode_config.funcs && dev->mode_config.funcs->output_poll_changed)
                dev->mode_config.funcs->output_poll_changed(dev);
 
        drm_client_dev_hotplug(dev);

It appears that pointer is set in msm_drm_init(), which suggests events
are being forwarded before the driver is ready.

Johan

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

* [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
  2022-08-18  8:05   ` Krzysztof Kozlowski
  2022-10-24 20:02   ` Andrew Halaney
@ 2022-10-26 19:52   ` Subbaraman Narayanamurthy
  2022-11-11 12:56   ` Johan Hovold
  2022-11-11 12:57   ` Johan Hovold
  4 siblings, 0 replies; 18+ messages in thread
From: Subbaraman Narayanamurthy @ 2022-10-26 19:52 UTC (permalink / raw)
  To: bjorn.andersson
  Cc: agross, devicetree, konrad.dybcio, krzysztof.kozlowski+dt,
	linux-arm-msm, linux-kernel, linux-pm, robh+dt, sre

> +static struct pmic_glink *__pmic_glink;
> +static DEFINE_MUTEX(__pmic_glink_lock);

Having this global device and lock to update pmic_glink struct pointer
might not work well if there are multiple pmic_glink devices that
corresponds to multiple pmic_glink channels (or rpmsg devices). This
is fine for the primary pmic_glink channel that supports mission mode
clients. However, to support debugging, there could be a secondary
pmic_glink channel and some clients under it.

> +struct pmic_glink_client {
> +	struct list_head node;
> +
> +	struct pmic_glink *pmic;

It would be good to name this pointer as "pg" or something.

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

* Re: [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
                     ` (2 preceding siblings ...)
  2022-10-26 19:52   ` Subbaraman Narayanamurthy
@ 2022-11-11 12:56   ` Johan Hovold
  2022-11-11 12:57   ` Johan Hovold
  4 siblings, 0 replies; 18+ messages in thread
From: Johan Hovold @ 2022-11-11 12:56 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote:
> The PMIC GLINK service runs on one of the co-processors of some modern
> Qualcomm platforms and implements USB-C and battery managements. It uses
> a message based protocol over GLINK for communication with the OS, hence
> the name.
> 
> The driver implemented provides the rpmsg device for communication and
> uses auxilirary bus to spawn off individual devices in respsective
> subsystem. The auxilirary devices are spawned off from a
> platform_device, so that the drm_bridge is available early, to allow the
> DisplayPort driver to probe even before the remoteproc has spun up.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  drivers/soc/qcom/Kconfig            |  14 ++
>  drivers/soc/qcom/Makefile           |   1 +
>  drivers/soc/qcom/pmic_glink.c       | 336 ++++++++++++++++++++++++++++
>  include/linux/soc/qcom/pmic_glink.h |  32 +++
>  4 files changed, 383 insertions(+)
>  create mode 100644 drivers/soc/qcom/pmic_glink.c
>  create mode 100644 include/linux/soc/qcom/pmic_glink.h
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index e0d7a5459562..2289f5e0d5ad 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS
>  	tristate
>  	select QCOM_QMI_HELPERS
>  
> +config QCOM_PMIC_GLINK
> +	tristate "Qualcomm PMIC GLINK driver"
> +	depends on RPMSG
> +	depends on TYPEC
> +	depends on DRM

You should add

	select AUXILIARY_BUS

here as this driver will not compile without it. Then you can drop the
corresponding select from the battery driver.

> +	select QCOM_PDR_HELPERS
> +	help
> +	  The Qualcomm PMIC GLINK driver provides access, over GLINK, to the
> +	  USB and battery firmware running on one of the coprocessors in
> +	  several modern Qualcomm platforms.
> +
> +	  Say yes here to support USB-C and battery status on modern Qualcomm
> +	  platforms.

Johan

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

* Re: [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
  2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
                     ` (3 preceding siblings ...)
  2022-11-11 12:56   ` Johan Hovold
@ 2022-11-11 12:57   ` Johan Hovold
  4 siblings, 0 replies; 18+ messages in thread
From: Johan Hovold @ 2022-11-11 12:57 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, linux-arm-msm, devicetree, linux-kernel,
	linux-pm

[ Resending to Bjorn's current address. ]

On Wed, Aug 17, 2022 at 08:15:10PM -0700, Bjorn Andersson wrote:
> The PMIC GLINK service runs on one of the co-processors of some modern
> Qualcomm platforms and implements USB-C and battery managements. It uses
> a message based protocol over GLINK for communication with the OS, hence
> the name.
> 
> The driver implemented provides the rpmsg device for communication and
> uses auxilirary bus to spawn off individual devices in respsective
> subsystem. The auxilirary devices are spawned off from a
> platform_device, so that the drm_bridge is available early, to allow the
> DisplayPort driver to probe even before the remoteproc has spun up.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  drivers/soc/qcom/Kconfig            |  14 ++
>  drivers/soc/qcom/Makefile           |   1 +
>  drivers/soc/qcom/pmic_glink.c       | 336 ++++++++++++++++++++++++++++
>  include/linux/soc/qcom/pmic_glink.h |  32 +++
>  4 files changed, 383 insertions(+)
>  create mode 100644 drivers/soc/qcom/pmic_glink.c
>  create mode 100644 include/linux/soc/qcom/pmic_glink.h
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index e0d7a5459562..2289f5e0d5ad 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -91,6 +91,20 @@ config QCOM_PDR_HELPERS
>  	tristate
>  	select QCOM_QMI_HELPERS
>  
> +config QCOM_PMIC_GLINK
> +	tristate "Qualcomm PMIC GLINK driver"
> +	depends on RPMSG
> +	depends on TYPEC
> +	depends on DRM

You should add

	select AUXILIARY_BUS

here as this driver will not compile without it. Then you can drop the
corresponding select from the battery driver.

> +	select QCOM_PDR_HELPERS
> +	help
> +	  The Qualcomm PMIC GLINK driver provides access, over GLINK, to the
> +	  USB and battery firmware running on one of the coprocessors in
> +	  several modern Qualcomm platforms.
> +
> +	  Say yes here to support USB-C and battery status on modern Qualcomm
> +	  platforms.

Johan

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

* Re: [PATCH 0/4] soc: qcom: Introduce PMIC GLINK
  2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
                   ` (4 preceding siblings ...)
  2022-08-18 19:25 ` [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Steev Klimaszewski
@ 2023-02-06 22:30 ` Bjorn Andersson
  5 siblings, 0 replies; 18+ messages in thread
From: Bjorn Andersson @ 2023-02-06 22:30 UTC (permalink / raw)
  To: Andy Gross, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Sebastian Reichel, Bjorn Andersson
  Cc: linux-kernel, linux-pm, linux-arm-msm, devicetree

On Wed, 17 Aug 2022 20:15:08 -0700, Bjorn Andersson wrote:
> This implements the base PMIC GLINK driver, a power_supply driver and a driver
> for the USB Type-C altmode protocol. This has been tested and shown to provide
> battery information, USB Type-C switch and mux requests and DisplayPort
> notifications on SC8180X, SC8280XP and SM8350.
> 
> Bjorn Andersson (4):
>   dt-bindings: soc: qcom: Introduce PMIC GLINK binding
>   soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
>   soc: qcom: pmic_glink: Introduce altmode support
>   power: supply: Introduce Qualcomm PMIC GLINK power supply
> 
> [...]

Applied, thanks!

[1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding
      commit: 68d868adc121f68edde0f4c0e16923103b868945
[2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver
      commit: 58ef4ece1e41ac525db3e79529909683325d85df
[3/4] soc: qcom: pmic_glink: Introduce altmode support
      commit: 080b4e24852b1d5b66929f69344e6c3eeb963941
[4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply
      (no commit info)

Best regards,
-- 
Bjorn Andersson <andersson@kernel.org>

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

end of thread, other threads:[~2023-02-06 22:28 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-18  3:15 [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Bjorn Andersson
2022-08-18  3:15 ` [PATCH 1/4] dt-bindings: soc: qcom: Introduce PMIC GLINK binding Bjorn Andersson
2022-08-18  7:55   ` Krzysztof Kozlowski
2022-08-22 12:39   ` Rob Herring
2022-08-18  3:15 ` [PATCH 2/4] soc: qcom: pmic_glink: Introduce base PMIC GLINK driver Bjorn Andersson
2022-08-18  8:05   ` Krzysztof Kozlowski
2022-10-24 20:02   ` Andrew Halaney
2022-10-26 19:52   ` Subbaraman Narayanamurthy
2022-11-11 12:56   ` Johan Hovold
2022-11-11 12:57   ` Johan Hovold
2022-08-18  3:15 ` [PATCH 3/4] soc: qcom: pmic_glink: Introduce altmode support Bjorn Andersson
2022-10-25  8:24   ` Johan Hovold
2022-10-25  8:32   ` Johan Hovold
2022-08-18  3:15 ` [PATCH 4/4] power: supply: Introduce Qualcomm PMIC GLINK power supply Bjorn Andersson
2022-08-30 15:25   ` Johan Hovold
2022-09-20  2:49   ` Steev Klimaszewski
2022-08-18 19:25 ` [PATCH 0/4] soc: qcom: Introduce PMIC GLINK Steev Klimaszewski
2023-02-06 22:30 ` Bjorn Andersson

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).