2024-05-27 10:04:12

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 0/6] power: supply: Lenovo Yoga C630 EC

This adds binding, driver and the DT support for the Lenovo Yoga C630
Embedded Controller, to provide battery information.

Support for this EC was implemented by Bjorn, who later could not work
on this driver. I've picked this patchset up and updated it following
the pending review comments.

DisplayPort support is still not a part of this patchset. It uses EC
messages to provide AltMode information rather than implementing
corresponding UCSI commands. However to have a cleaner uAPI story, the
AltMode should be handled via the same Type-C port.

Merge strategy: the driver bits depend on the platform/arm64 patch,
which adds interface for the subdrivers. I'd either ask to get that
patch merged to the immutable branch, which then can be picked up by
power/supply and USB trees or, to make life simpler, ack merging all
driver bits e.g. through USB subsystem (I'm biased here since I plan to
send more cleanups for the UCSI subsystem, which would otherwise result
in cross-subsystem conflicts).

---
Changes in v3:
- Split the driver into core and power supply drivers,
- Added UCSI driver part, handling USB connections,
- Fixed Bjorn's address in DT bindings (Brian Masney)
- Changed power-role for both ports to be "dual" per UCSI
- Link to v2: https://lore.kernel.org/linux-arm-msm/[email protected]/

Changes in v2:
- Dropped DP support for now, as the bindings are in process of being
discussed separately,
- Merged dt patch into the same patchseries,
- Removed the fixed serial number battery property,
- Fixed indentation of dt bindings example,
- Added property: reg and unevaluatedProperties to the connector
bindings.
- Link to v1: https://lore.kernel.org/linux-arm-msm/[email protected]/

---
Bjorn Andersson (2):
dt-bindings: power: supply: Add Lenovo Yoga C630 EC
arm64: dts: qcom: c630: Add Embedded Controller node

Dmitry Baryshkov (4):
platform: arm64: add Lenovo Yoga C630 WOS EC driver
usb: typec: ucsi: add Lenovo Yoga C630 glue driver
power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver
arm64: dts: qcom: sdm845: describe connections of USB/DP port

.../bindings/power/supply/lenovo,yoga-c630-ec.yaml | 83 ++++
arch/arm64/boot/dts/qcom/sdm845.dtsi | 53 ++-
.../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++
drivers/platform/arm64/Kconfig | 14 +
drivers/platform/arm64/Makefile | 1 +
drivers/platform/arm64/lenovo-yoga-c630.c | 279 ++++++++++++
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lenovo_yoga_c630_battery.c | 476 +++++++++++++++++++++
drivers/usb/typec/ucsi/Kconfig | 9 +
drivers/usb/typec/ucsi/Makefile | 1 +
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 189 ++++++++
include/linux/platform_data/lenovo-yoga-c630.h | 42 ++
13 files changed, 1232 insertions(+), 1 deletion(-)
---
base-commit: 8314289a8d50a4e05d8ece1ae0445a3b57bb4d3b
change-id: 20240527-yoga-ec-driver-76fd7f5ddae8

Best regards,
--
Dmitry Baryshkov <[email protected]>



2024-05-27 10:04:26

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 1/6] dt-bindings: power: supply: Add Lenovo Yoga C630 EC

From: Bjorn Andersson <[email protected]>

Add binding for the Embedded Controller found in the Qualcomm
Snapdragon-based Lenovo Yoga C630.

Signed-off-by: Bjorn Andersson <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
.../bindings/power/supply/lenovo,yoga-c630-ec.yaml | 83 ++++++++++++++++++++++
1 file changed, 83 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
new file mode 100644
index 000000000000..52a302850743
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/lenovo,yoga-c630-ec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Lenovo Yoga C630 Embedded Controller.
+
+maintainers:
+ - Bjorn Andersson <[email protected]>
+
+description:
+ The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller
+ (EC) which handles things such as battery and USB Type-C. This binding
+ describes the interface, on an I2C bus, to this EC.
+
+properties:
+ compatible:
+ const: lenovo,yoga-c630-ec
+
+ reg:
+ const: 0x70
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ interrupts:
+ maxItems: 1
+
+patternProperties:
+ '^connector@[01]$':
+ $ref: /schemas/connector/usb-connector.yaml#
+
+ properties:
+ reg:
+ maxItems: 1
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |+
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c1 {
+ clock-frequency = <400000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ embedded-controller@70 {
+ compatible = "lenovo,yoga-c630-ec";
+ reg = <0x70>;
+
+ interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ connector@0 {
+ compatible = "usb-c-connector";
+ reg = <0>;
+ power-role = "source";
+ data-role = "host";
+ };
+
+ connector@1 {
+ compatible = "usb-c-connector";
+ reg = <1>;
+ power-role = "source";
+ data-role = "host";
+ };
+ };
+ };
+...

--
2.39.2


2024-05-27 10:05:11

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 3/6] usb: typec: ucsi: add Lenovo Yoga C630 glue driver

The Lenovo Yoga C630 WOS laptop provides implements UCSI interface in
the onboard EC. Add glue driver to interface the platform's UCSI
implementation.

Signed-off-by: Dmitry Baryshkov <[email protected]>
---
drivers/usb/typec/ucsi/Kconfig | 9 ++
drivers/usb/typec/ucsi/Makefile | 1 +
drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 189 ++++++++++++++++++++++++++++++++
3 files changed, 199 insertions(+)

diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index bdcb1764cfae..680e1b87b152 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -69,4 +69,13 @@ config UCSI_PMIC_GLINK
To compile the driver as a module, choose M here: the module will be
called ucsi_glink.

+config UCSI_LENOVO_YOGA_C630
+ tristate "UCSI Interface Driver for Lenovo Yoga C630"
+ depends on EC_LENOVO_YOGA_C630
+ help
+ This driver enables UCSI support on the Lenovo Yoga C630 laptop.
+
+ To compile the driver as a module, choose M here: the module will be
+ called ucsi_yoga_c630.
+
endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b4679f94696b..aed41d23887b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
+obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
new file mode 100644
index 000000000000..ca1ab5c81b87
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/module.h>
+#include <linux/platform_data/lenovo-yoga-c630.h>
+
+#include "ucsi.h"
+
+struct yoga_c630_ucsi {
+ struct yoga_c630_ec *ec;
+ struct ucsi *ucsi;
+ struct notifier_block nb;
+ struct completion complete;
+ unsigned long flags;
+#define UCSI_C630_COMMAND_PENDING 0
+#define UCSI_C630_ACK_PENDING 1
+ u16 version;
+};
+
+static int yoga_c630_ucsi_read(struct ucsi *ucsi, unsigned int offset,
+ void *val, size_t val_len)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[YOGA_C630_UCSI_READ_SIZE];
+ int ret;
+
+ ret = yoga_c630_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ if (offset == UCSI_VERSION) {
+ memcpy(val, &uec->version, min(val_len, sizeof(uec->version)));
+ return 0;
+ }
+
+ if (offset == UCSI_CCI)
+ memcpy(val, buf,
+ min(val_len, YOGA_C630_UCSI_CCI_SIZE));
+ else if (offset == UCSI_MESSAGE_IN)
+ memcpy(val, buf + YOGA_C630_UCSI_CCI_SIZE,
+ min(val_len, YOGA_C630_UCSI_DATA_SIZE));
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_async_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ if (offset != UCSI_CONTROL ||
+ val_len != YOGA_C630_UCSI_WRITE_SIZE)
+ return -EINVAL;
+
+ return yoga_c630_ec_ucsi_write(uec->ec, val);
+}
+
+static int yoga_c630_ucsi_sync_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
+ int ret;
+
+ if (ack)
+ set_bit(UCSI_C630_ACK_PENDING, &uec->flags);
+ else
+ set_bit(UCSI_C630_COMMAND_PENDING, &uec->flags);
+
+ reinit_completion(&uec->complete);
+
+ ret = yoga_c630_ucsi_async_write(ucsi, offset, val, val_len);
+ if (ret)
+ goto out_clear_bit;
+
+ if (!wait_for_completion_timeout(&uec->complete, 5 * HZ))
+ ret = -ETIMEDOUT;
+
+out_clear_bit:
+ if (ack)
+ clear_bit(UCSI_C630_ACK_PENDING, &uec->flags);
+ else
+ clear_bit(UCSI_C630_COMMAND_PENDING, &uec->flags);
+
+ return ret;
+}
+
+const struct ucsi_operations yoga_c630_ucsi_ops = {
+ .read = yoga_c630_ucsi_read,
+ .sync_write = yoga_c630_ucsi_sync_write,
+ .async_write = yoga_c630_ucsi_async_write,
+};
+
+static int yoga_c630_ucsi_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct yoga_c630_ucsi *uec = container_of(nb, struct yoga_c630_ucsi, nb);
+ u32 cci;
+ int ret;
+
+ if (action == LENOVO_EC_EVENT_USB || action == LENOVO_EC_EVENT_HPD) {
+ ucsi_connector_change(uec->ucsi, 1);
+ return NOTIFY_OK;
+ }
+
+ if (action != LENOVO_EC_EVENT_UCSI)
+ return NOTIFY_DONE;
+
+ ret = uec->ucsi->ops->read(uec->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ return NOTIFY_DONE;
+
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(uec->ucsi, UCSI_CCI_CONNECTOR(cci));
+
+ if (cci & UCSI_CCI_ACK_COMPLETE &&
+ test_bit(UCSI_C630_ACK_PENDING, &uec->flags))
+ complete(&uec->complete);
+ if (cci & UCSI_CCI_COMMAND_COMPLETE &&
+ test_bit(UCSI_C630_COMMAND_PENDING, &uec->flags))
+ complete(&uec->complete);
+
+ return NOTIFY_OK;
+}
+
+static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct yoga_c630_ec *ec = adev->dev.platform_data;
+ struct yoga_c630_ucsi *uec;
+ int ret;
+
+ uec = devm_kzalloc(&adev->dev, sizeof(*uec), GFP_KERNEL);
+ if (!uec)
+ return -ENOMEM;
+
+ uec->ec = ec;
+ init_completion(&uec->complete);
+ uec->nb.notifier_call = yoga_c630_ucsi_notify;
+
+ uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
+ if (IS_ERR(uec->ucsi))
+ return PTR_ERR(uec->ucsi);
+
+ ucsi_set_drvdata(uec->ucsi, uec);
+
+ uec->version = yoga_c630_ec_ucsi_get_version(uec->ec);
+
+ auxiliary_set_drvdata(adev, uec);
+
+ ret = yoga_c630_ec_register_notify(ec, &uec->nb);
+ if (ret)
+ return ret;
+
+ return ucsi_register(uec->ucsi);
+}
+
+static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
+{
+ struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
+
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_unregister(uec->ucsi);
+}
+
+static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
+ { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_UCSI, },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, yoga_c630_ucsi_id_table);
+
+static struct auxiliary_driver yoga_c630_ucsi_driver = {
+ .name = YOGA_C630_DEV_UCSI,
+ .id_table = yoga_c630_ucsi_id_table,
+ .probe = yoga_c630_ucsi_probe,
+ .remove = yoga_c630_ucsi_remove,
+};
+
+module_auxiliary_driver(yoga_c630_ucsi_driver);
+
+MODULE_DESCRIPTION("Lenovo Yoga C630 UCSI");
+MODULE_LICENSE("GPL");

--
2.39.2


2024-05-27 10:05:14

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 2/6] platform: arm64: add Lenovo Yoga C630 WOS EC driver

Lenovo Yoga C630 WOS is a laptop using Snapdragon 850 SoC. Like many
laptops it uses embedded controller (EC) to perform various platform
operations, including, but not limited, to Type-C port control or power
supply handlng.

Add the driver for the EC, that creates devices for UCSI and power
supply devices.

Signed-off-by: Dmitry Baryshkov <[email protected]>
---
drivers/platform/arm64/Kconfig | 14 ++
drivers/platform/arm64/Makefile | 1 +
drivers/platform/arm64/lenovo-yoga-c630.c | 279 +++++++++++++++++++++++++
include/linux/platform_data/lenovo-yoga-c630.h | 42 ++++
4 files changed, 336 insertions(+)

diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig
index 8fdca0f8e909..8c103b3150d1 100644
--- a/drivers/platform/arm64/Kconfig
+++ b/drivers/platform/arm64/Kconfig
@@ -32,4 +32,18 @@ config EC_ACER_ASPIRE1
laptop where this information is not properly exposed via the
standard ACPI devices.

+config EC_LENOVO_YOGA_C630
+ tristate "Lenovo Yoga C630 Embedded Controller driver"
+ depends on I2C
+ help
+ Driver for the Embedded Controller in the Qualcomm Snapdragon-based
+ Lenovo Yoga C630, which provides battery and power adapter
+ information.
+
+ This driver provides battery and AC status support for the mentioned
+ laptop where this information is not properly exposed via the
+ standard ACPI devices.
+
+ Say M or Y here to include this support.
+
endif # ARM64_PLATFORM_DEVICES
diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile
index 4fcc9855579b..b2ae9114fdd8 100644
--- a/drivers/platform/arm64/Makefile
+++ b/drivers/platform/arm64/Makefile
@@ -6,3 +6,4 @@
#

obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
+obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c
new file mode 100644
index 000000000000..3d1d5acde807
--- /dev/null
+++ b/drivers/platform/arm64/lenovo-yoga-c630.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/lenovo-yoga-c630.h>
+
+#define LENOVO_EC_RESPONSE_REG 0x01
+#define LENOVO_EC_REQUEST_REG 0x02
+
+#define LENOVO_EC_UCSI_WRITE 0x20
+#define LENOVO_EC_UCSI_READ 0x21
+
+#define LENOVO_EC_READ_REG 0xb0
+#define LENOVO_EC_REQUEST_NEXT_EVENT 0x84
+
+struct yoga_c630_ec {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct blocking_notifier_head notifier_list;
+};
+
+static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len,
+ u8 *resp, size_t resp_len)
+{
+ int ret;
+
+ WARN_ON(!mutex_is_locked(&ec->lock));
+
+ ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_REQUEST_REG,
+ req_len, req);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_RESPONSE_REG,
+ resp_len, resp);
+}
+
+int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr)
+{
+ u8 req[2] = { LENOVO_EC_READ_REG, };
+ int ret;
+ u8 val;
+
+ mutex_lock(&ec->lock);
+ req[1] = addr;
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &val, 1);
+ mutex_unlock(&ec->lock);
+
+ return ret < 0 ? ret : val;
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_read8);
+
+int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr)
+{
+ u8 req[2] = { LENOVO_EC_READ_REG, };
+ int ret;
+ u8 msb;
+ u8 lsb;
+
+ mutex_lock(&ec->lock);
+
+ req[1] = addr;
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1);
+ if (ret < 0)
+ goto out;
+
+ req[1] = addr + 1;
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1);
+
+out:
+ mutex_unlock(&ec->lock);
+
+ return ret < 0 ? ret : msb << 8 | lsb;
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_read16);
+
+u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec)
+{
+ u8 req[3] = { 0xb3, 0xf2, 0x20};
+ int ret;
+ u8 msb;
+ u8 lsb;
+
+ mutex_lock(&ec->lock);
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1);
+ if (ret < 0)
+ goto out;
+
+ req[2]++;
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1);
+
+out:
+ mutex_unlock(&ec->lock);
+
+ return ret < 0 ? ret : msb << 8 | lsb;
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_get_version);
+
+int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec,
+ const u8 req[YOGA_C630_UCSI_WRITE_SIZE])
+{
+ int ret;
+
+ mutex_lock(&ec->lock);
+ ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_UCSI_WRITE,
+ YOGA_C630_UCSI_WRITE_SIZE, req);
+ mutex_unlock(&ec->lock);
+
+ return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_write);
+
+int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec,
+ u8 resp[YOGA_C630_UCSI_READ_SIZE])
+{
+ int ret;
+
+ mutex_lock(&ec->lock);
+ ret = i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_UCSI_READ,
+ YOGA_C630_UCSI_READ_SIZE, resp);
+ mutex_unlock(&ec->lock);
+
+ return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_read);
+
+static irqreturn_t yoga_c630_ec_intr(int irq, void *data)
+{
+ u8 req[] = { LENOVO_EC_REQUEST_NEXT_EVENT };
+ struct yoga_c630_ec *ec = data;
+ u8 event;
+ int ret;
+
+ mutex_lock(&ec->lock);
+ ret = yoga_c630_ec_request(ec, req, sizeof(req), &event, 1);
+ mutex_unlock(&ec->lock);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ pr_info("NOTIFY %x\n", event);
+
+ blocking_notifier_call_chain(&ec->notifier_list, event, ec);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * yoga_c630_ec_register_notify - Register a notifier callback for EC events.
+ * @ec: Yoga C630 EC
+ * @nb: Notifier block pointer to register
+ *
+ * Return: 0 on success or negative error code.
+ */
+int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&ec->notifier_list,
+ nb);
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_register_notify);
+
+/**
+ * yoga_c630_ec_unregister_notify - Unregister notifier callback for EC events.
+ * @ec: Yoga C630 EC
+ * @nb: Notifier block pointer to unregister
+ *
+ * Unregister a notifier callback that was previously registered with
+ * yoga_c630_ec_register_notify().
+ */
+void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&ec->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify);
+
+static void yoga_c630_aux_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(adev);
+}
+
+static void yoga_c630_aux_remove(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static int yoga_c630_aux_init(struct device *parent, const char *name,
+ struct yoga_c630_ec *ec)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = name;
+ adev->id = 0;
+ adev->dev.parent = parent;
+ adev->dev.release = yoga_c630_aux_release;
+ adev->dev.platform_data = ec;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ret;
+ }
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev);
+}
+
+static int yoga_c630_ec_probe(struct i2c_client *client)
+{
+ struct yoga_c630_ec *ec;
+ struct device *dev = &client->dev;
+ int ret;
+
+ ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ mutex_init(&ec->lock);
+ ec->client = client;
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
+
+ ret = devm_request_threaded_irq(dev, client->irq,
+ NULL, yoga_c630_ec_intr,
+ IRQF_ONESHOT, "yoga_c630_ec", ec);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "unable to request irq\n");
+
+ ret = yoga_c630_aux_init(dev, YOGA_C630_DEV_PSY, ec);
+ if (ret)
+ return ret;
+
+ return yoga_c630_aux_init(dev, YOGA_C630_DEV_UCSI, ec);
+}
+
+
+static const struct of_device_id yoga_c630_ec_of_match[] = {
+ { .compatible = "lenovo,yoga-c630-ec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match);
+
+static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = {
+ { "yoga-c630-ec", },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table);
+
+static struct i2c_driver yoga_c630_ec_i2c_driver = {
+ .driver = {
+ .name = "yoga-c630-ec",
+ .of_match_table = yoga_c630_ec_of_match
+ },
+ .probe = yoga_c630_ec_probe,
+ .id_table = yoga_c630_ec_i2c_id_table,
+};
+module_i2c_driver(yoga_c630_ec_i2c_driver);
+
+MODULE_DESCRIPTION("Lenovo Yoga C630 Embedded Controller");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/lenovo-yoga-c630.h b/include/linux/platform_data/lenovo-yoga-c630.h
new file mode 100644
index 000000000000..2b893dbeb124
--- /dev/null
+++ b/include/linux/platform_data/lenovo-yoga-c630.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+
+#ifndef _LENOVO_YOGA_C630_DATA_H
+#define _LENOVO_YOGA_C630_DATA_H
+
+struct yoga_c630_ec;
+
+#define YOGA_C630_MOD_NAME "lenovo_yoga_c630"
+
+#define YOGA_C630_DEV_UCSI "ucsi"
+#define YOGA_C630_DEV_PSY "psy"
+
+int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr);
+int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr);
+
+int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb);
+void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb);
+
+#define YOGA_C630_UCSI_WRITE_SIZE 8
+#define YOGA_C630_UCSI_CCI_SIZE 4
+#define YOGA_C630_UCSI_DATA_SIZE 16
+#define YOGA_C630_UCSI_READ_SIZE (YOGA_C630_UCSI_CCI_SIZE + YOGA_C630_UCSI_DATA_SIZE)
+u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec);
+int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec,
+ const u8 req[YOGA_C630_UCSI_WRITE_SIZE]);
+int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec,
+ u8 resp[YOGA_C630_UCSI_READ_SIZE]);
+
+#define LENOVO_EC_EVENT_USB 0x20
+#define LENOVO_EC_EVENT_UCSI 0x21
+#define LENOVO_EC_EVENT_HPD 0x22
+#define LENOVO_EC_EVENT_BAT_STATUS 0x24
+#define LENOVO_EC_EVENT_BAT_INFO 0x25
+#define LENOVO_EC_EVENT_BAT_ADPT_STATUS 0x37
+
+#endif

--
2.39.2


2024-05-27 10:05:37

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 5/6] arm64: dts: qcom: sdm845: describe connections of USB/DP port

Describe links between the first USB3 host and the DisplayPort that is
routed to the same pins.

Signed-off-by: Dmitry Baryshkov <[email protected]>
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 53 +++++++++++++++++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 10de2bd46ffc..745518e3ded3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -4028,6 +4028,35 @@ usb_1_qmpphy: phy@88e8000 {

#clock-cells = <1>;
#phy-cells = <1>;
+ orientation-switch;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ usb_1_qmpphy_out: endpoint {
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ usb_1_qmpphy_usb_ss_in: endpoint {
+ remote-endpoint = <&usb_1_dwc3_ss>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ usb_1_qmpphy_dp_in: endpoint {
+ remote-endpoint = <&dp_out>;
+ };
+ };
+ };
};

usb_2_qmpphy: phy@88eb000 {
@@ -4108,6 +4137,26 @@ usb_1_dwc3: usb@a600000 {
snps,dis_enblslpm_quirk;
phys = <&usb_1_hsphy>, <&usb_1_qmpphy QMP_USB43DP_USB3_PHY>;
phy-names = "usb2-phy", "usb3-phy";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ usb_1_dwc3_hs: endpoint {
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ usb_1_dwc3_ss: endpoint {
+ remote-endpoint = <&usb_1_qmpphy_usb_ss_in>;
+ };
+ };
+ };
};
};

@@ -4598,7 +4647,9 @@ dp_in: endpoint {

port@1 {
reg = <1>;
- dp_out: endpoint { };
+ dp_out: endpoint {
+ remote-endpoint = <&usb_1_qmpphy_dp_in>;
+ };
};
};


--
2.39.2


2024-05-27 10:05:52

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 6/6] arm64: dts: qcom: c630: Add Embedded Controller node

From: Bjorn Andersson <[email protected]>

The Embedded Controller in the Lenovo Yoga C630 is accessible on &i2c1
and provides battery and adapter status, as well as altmode
notifications for the second USB Type-C port.

Add a definition for the EC.

Signed-off-by: Bjorn Andersson <[email protected]>
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
.../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++++++++++++++++++++
1 file changed, 76 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index 47dc42f6e936..d975f78eb3ab 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -370,6 +370,66 @@ zap-shader {
&i2c1 {
status = "okay";
clock-frequency = <400000>;
+
+ embedded-controller@70 {
+ compatible = "lenovo,yoga-c630-ec";
+ reg = <0x70>;
+
+ interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&ec_int_state>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ connector@0 {
+ compatible = "usb-c-connector";
+ reg = <0>;
+ power-role = "dual";
+ data-role = "host";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ ucsi0_hs_in: endpoint {
+ remote-endpoint = <&usb_1_dwc3_hs>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ ucsi0_ss_in: endpoint {
+ remote-endpoint = <&usb_1_qmpphy_out>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ ucsi0_sbu: endpoint {
+ };
+ };
+ };
+ };
+
+ connector@1 {
+ compatible = "usb-c-connector";
+ reg = <1>;
+ power-role = "dual";
+ data-role = "host";
+
+ /*
+ * connected to the onboard USB hub, orientation is
+ * handled by the controller
+ */
+ };
+ };
};

&i2c3 {
@@ -694,6 +754,14 @@ mode_pin_active: mode-pin-state {

bias-disable;
};
+
+ ec_int_state: ec-int-state {
+ pins = "gpio20";
+ function = "gpio";
+
+ input-enable;
+ bias-disable;
+ };
};

&uart6 {
@@ -741,6 +809,10 @@ &usb_1_dwc3 {
dr_mode = "host";
};

+&usb_1_dwc3_hs {
+ remote-endpoint = <&ucsi0_hs_in>;
+};
+
&usb_1_hsphy {
status = "okay";

@@ -761,6 +833,10 @@ &usb_1_qmpphy {
vdda-pll-supply = <&vdda_usb1_ss_core>;
};

+&usb_1_qmpphy_out {
+ remote-endpoint = <&ucsi0_ss_in>;
+};
+
&usb_2 {
status = "okay";
};

--
2.39.2


2024-05-27 10:07:06

by Dmitry Baryshkov

[permalink] [raw]
Subject: [PATCH v3 4/6] power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver

On the Lenovo Yoga C630 WOS laptop the EC provides access to the adapter
and battery status. Add the driver to read power supply status on the
laptop.

Signed-off-by: Dmitry Baryshkov <[email protected]>
---
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lenovo_yoga_c630_battery.c | 476 ++++++++++++++++++++++++
3 files changed, 486 insertions(+)

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 3e31375491d5..55ab8e90747d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -167,6 +167,15 @@ config BATTERY_LEGO_EV3
help
Say Y here to enable support for the LEGO MINDSTORMS EV3 battery.

+config BATTERY_LENOVO_YOGA_C630
+ tristate "Lenovo Yoga C630 battery"
+ depends on OF && EC_LENOVO_YOGA_C630
+ help
+ This driver enables battery support on the Lenovo Yoga C630 laptop.
+
+ To compile the driver as a module, choose M here: the module will be
+ called lenovo_yoga_c630_battery.
+
config BATTERY_PMU
tristate "Apple PMU battery"
depends on PPC32 && ADB_PMU
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 58b567278034..8ebbdcf92dac 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
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_LENOVO_YOGA_C630) += lenovo_yoga_c630_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_QCOM_BATTMGR) += qcom_battmgr.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
diff --git a/drivers/power/supply/lenovo_yoga_c630_battery.c b/drivers/power/supply/lenovo_yoga_c630_battery.c
new file mode 100644
index 000000000000..f5cc02f36bfc
--- /dev/null
+++ b/drivers/power/supply/lenovo_yoga_c630_battery.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_data/lenovo-yoga-c630.h>
+#include <linux/power_supply.h>
+
+struct yoga_c630_psy {
+ struct yoga_c630_ec *ec;
+ struct device *dev;
+ struct device_node *of_node;
+ struct notifier_block nb;
+ struct mutex lock;
+
+ struct power_supply *adp_psy;
+ struct power_supply *bat_psy;
+
+ unsigned long last_status_update;
+
+ bool adapter_online;
+
+ bool unit_mA;
+
+ unsigned int scale;
+
+ bool bat_present;
+ unsigned int bat_status;
+ unsigned int design_capacity;
+ unsigned int design_voltage;
+ unsigned int full_charge_capacity;
+
+ unsigned int capacity_now;
+ unsigned int voltage_now;
+
+ int current_now;
+ int rate_now;
+};
+
+#define LENOVO_EC_CACHE_TIME (10 * HZ)
+
+#define LENOVO_EC_ADPT_STATUS 0xa3
+#define LENOVO_EC_ADPT_PRESENT BIT(7)
+#define LENOVO_EC_BAT_ATTRIBUTES 0xc0
+#define LENOVO_EC_BAT_ATTR_UNIT_IS_MA BIT(1)
+#define LENOVO_EC_BAT_STATUS 0xc1
+#define LENOVO_EC_BAT_REMAIN_CAPACITY 0xc2
+#define LENOVO_EC_BAT_VOLTAGE 0xc6
+#define LENOVO_EC_BAT_DESIGN_VOLTAGE 0xc8
+#define LENOVO_EC_BAT_DESIGN_CAPACITY 0xca
+#define LENOVO_EC_BAT_FULL_CAPACITY 0xcc
+#define LENOVO_EC_BAT_CURRENT 0xd2
+#define LENOVO_EC_BAT_FULL_FACTORY 0xd6
+#define LENOVO_EC_BAT_PRESENT 0xda
+#define LENOVO_EC_BAT_FULL_REGISTER 0xdb
+#define LENOVO_EC_BAT_FULL_IS_FACTORY BIT(0)
+
+/* the mutex should already be locked */
+static int yoga_c630_psy_update_bat_info(struct yoga_c630_psy *ecbat)
+{
+ struct yoga_c630_ec *ec = ecbat->ec;
+ int val;
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_PRESENT);
+ if (val < 0)
+ return val;
+ ecbat->bat_present = !!(val & BIT(0));
+ if (!ecbat->bat_present)
+ return val;
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_ATTRIBUTES);
+ if (val < 0)
+ return val;
+ ecbat->unit_mA = val & LENOVO_EC_BAT_ATTR_UNIT_IS_MA;
+ if (ecbat->unit_mA)
+ ecbat->scale = 1;
+ else
+ ecbat->scale = 10;
+
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_CAPACITY);
+ if (val < 0)
+ return val;
+ ecbat->design_capacity = val * ecbat->scale * 1000;
+
+ msleep(50);
+
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_DESIGN_VOLTAGE);
+ if (val < 0)
+ return val;
+ ecbat->design_voltage = val;
+
+ msleep(50);
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_FULL_REGISTER);
+ if (val < 0)
+ return val;
+ if (val & LENOVO_EC_BAT_FULL_IS_FACTORY)
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_FULL_FACTORY);
+ else
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_FULL_CAPACITY);
+ if (val < 0)
+ return val;
+ ecbat->full_charge_capacity = val * ecbat->scale * 1000;
+
+ return 0;
+}
+
+/* the mutex should already be locked */
+static int yoga_c630_psy_maybe_update_bat_status(struct yoga_c630_psy *ecbat)
+{
+ struct yoga_c630_ec *ec = ecbat->ec;
+ int current_mA;
+ int val;
+
+ if (time_before(jiffies, ecbat->last_status_update + LENOVO_EC_CACHE_TIME))
+ return 0;
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_BAT_STATUS);
+ if (val < 0)
+ return val;
+ ecbat->bat_status = val;
+
+ msleep(50);
+
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_REMAIN_CAPACITY);
+ if (val < 0)
+ return val;
+ ecbat->capacity_now = val * ecbat->scale * 1000;
+
+ msleep(50);
+
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_VOLTAGE);
+ if (val < 0)
+ return val;
+ ecbat->voltage_now = val * 1000;
+
+ msleep(50);
+
+ val = yoga_c630_ec_read16(ec, LENOVO_EC_BAT_CURRENT);
+ if (val < 0)
+ return val;
+ current_mA = sign_extend32(val, 15);
+ ecbat->current_now = current_mA * 1000;
+ ecbat->rate_now = current_mA * (ecbat->voltage_now / 1000);
+
+ msleep(50);
+
+ ecbat->last_status_update = jiffies;
+
+ return 0;
+}
+
+static int yoga_c630_psy_update_adapter_status(struct yoga_c630_psy *ecbat)
+{
+ struct yoga_c630_ec *ec = ecbat->ec;
+ int val;
+
+ mutex_lock(&ecbat->lock);
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_ADPT_STATUS);
+ if (val > 0)
+ ecbat->adapter_online = FIELD_GET(LENOVO_EC_ADPT_PRESENT, val);
+
+ mutex_unlock(&ecbat->lock);
+
+ return val;
+}
+
+static bool yoga_c630_psy_is_charged(struct yoga_c630_psy *ecbat)
+{
+ if (ecbat->bat_status != 0)
+ return false;
+
+ if (ecbat->full_charge_capacity <= ecbat->capacity_now)
+ return true;
+
+ if (ecbat->design_capacity <= ecbat->capacity_now)
+ return true;
+
+ return false;
+}
+
+static int yoga_c630_psy_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ if (!ecbat->bat_present &&
+ psp != POWER_SUPPLY_PROP_PRESENT)
+ return -ENODEV;
+
+ mutex_lock(&ecbat->lock);
+ rc = yoga_c630_psy_maybe_update_bat_status(ecbat);
+ mutex_unlock(&ecbat->lock);
+
+ if (rc)
+ return rc;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (ecbat->bat_status & BIT(0))
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (ecbat->bat_status & BIT(1))
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (yoga_c630_psy_is_charged(ecbat))
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = ecbat->bat_present;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = ecbat->design_voltage;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval = ecbat->design_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = ecbat->full_charge_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ val->intval = ecbat->capacity_now;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ecbat->current_now;
+ break;
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ val->intval = ecbat->rate_now;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = ecbat->voltage_now;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "PABAS0241231";
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Compal";
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static enum power_supply_property yoga_c630_psy_bat_mA_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property yoga_c630_psy_bat_mWh_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mA = {
+ .name = "yoga-c630-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = yoga_c630_psy_bat_mA_properties,
+ .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mA_properties),
+ .get_property = yoga_c630_psy_bat_get_property,
+};
+
+static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mWh = {
+ .name = "yoga-c630-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = yoga_c630_psy_bat_mWh_properties,
+ .num_properties = ARRAY_SIZE(yoga_c630_psy_bat_mWh_properties),
+ .get_property = yoga_c630_psy_bat_get_property,
+};
+
+static int yoga_c630_psy_adpt_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct yoga_c630_psy *ecbat = power_supply_get_drvdata(psy);
+ int rc = 0;
+
+ yoga_c630_psy_update_adapter_status(ecbat);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ecbat->adapter_online;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static enum power_supply_property yoga_c630_psy_adpt_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc yoga_c630_psy_adpt_psy_desc = {
+ .name = "yoga-c630-adapter",
+ .type = POWER_SUPPLY_TYPE_USB_TYPE_C,
+ .properties = yoga_c630_psy_adpt_properties,
+ .num_properties = ARRAY_SIZE(yoga_c630_psy_adpt_properties),
+ .get_property = yoga_c630_psy_adpt_get_property,
+};
+
+static int yoga_c630_psy_register_bat_psy(struct yoga_c630_psy *ecbat)
+{
+ struct power_supply_config bat_cfg = {};
+
+ bat_cfg.drv_data = ecbat;
+ bat_cfg.of_node = ecbat->of_node;
+ if (ecbat->unit_mA)
+ ecbat->bat_psy = power_supply_register_no_ws(ecbat->dev, &yoga_c630_psy_bat_psy_desc_mA, &bat_cfg);
+ else
+ ecbat->bat_psy = power_supply_register_no_ws(ecbat->dev, &yoga_c630_psy_bat_psy_desc_mWh, &bat_cfg);
+ if (IS_ERR(ecbat->bat_psy)) {
+ dev_err(ecbat->dev, "failed to register battery supply\n");
+ return PTR_ERR(ecbat->bat_psy);
+ }
+
+ return 0;
+}
+
+static void yoga_c630_ec_refresh_bat_info(struct yoga_c630_psy *ecbat)
+{
+ bool current_unit;
+
+ mutex_lock(&ecbat->lock);
+ current_unit = ecbat->unit_mA;
+
+ yoga_c630_psy_update_bat_info(ecbat);
+
+ if (current_unit != ecbat->unit_mA) {
+ power_supply_unregister(ecbat->bat_psy);
+ yoga_c630_psy_register_bat_psy(ecbat);
+ }
+
+ mutex_unlock(&ecbat->lock);
+}
+
+static int yoga_c630_psy_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct yoga_c630_psy *ecbat = container_of(nb, struct yoga_c630_psy, nb);
+
+ switch (action) {
+ case LENOVO_EC_EVENT_BAT_INFO:
+ yoga_c630_ec_refresh_bat_info(ecbat);
+ break;
+ case LENOVO_EC_EVENT_BAT_ADPT_STATUS:
+ power_supply_changed(ecbat->adp_psy);
+ fallthrough;
+ case LENOVO_EC_EVENT_BAT_STATUS:
+ power_supply_changed(ecbat->bat_psy);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int yoga_c630_psy_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct yoga_c630_ec *ec = adev->dev.platform_data;
+ struct power_supply_config adp_cfg = {};
+ struct device *dev = &adev->dev;
+ struct yoga_c630_psy *ecbat;
+ int ret;
+
+ ecbat = devm_kzalloc(&adev->dev, sizeof(*ecbat), GFP_KERNEL);
+ if (!ecbat)
+ return -ENOMEM;
+
+ ecbat->ec = ec;
+ ecbat->dev = dev;
+ mutex_init(&ecbat->lock);
+ ecbat->of_node = adev->dev.parent->of_node;
+ ecbat->nb.notifier_call = yoga_c630_psy_notify;
+
+ auxiliary_set_drvdata(adev, ecbat);
+
+ adp_cfg.drv_data = ecbat;
+ adp_cfg.of_node = ecbat->of_node;
+ adp_cfg.supplied_to = (char **)&yoga_c630_psy_bat_psy_desc_mA.name;
+ adp_cfg.num_supplicants = 1;
+ ecbat->adp_psy = devm_power_supply_register_no_ws(dev, &yoga_c630_psy_adpt_psy_desc, &adp_cfg);
+ if (IS_ERR(ecbat->adp_psy)) {
+ dev_err(dev, "failed to register AC adapter supply\n");
+ return PTR_ERR(ecbat->adp_psy);
+ }
+
+ mutex_lock(&ecbat->lock);
+
+ ret = yoga_c630_psy_update_bat_info(ecbat);
+ if (ret)
+ goto err_unlock;
+
+ ret = yoga_c630_psy_register_bat_psy(ecbat);
+ if (ret)
+ goto err_unlock;
+
+ mutex_unlock(&ecbat->lock);
+
+ ret = yoga_c630_ec_register_notify(ecbat->ec, &ecbat->nb);
+ if (ret)
+ goto err_unreg_bat;
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&ecbat->lock);
+
+err_unreg_bat:
+ power_supply_unregister(ecbat->bat_psy);
+ return ret;
+}
+
+static void yoga_c630_psy_remove(struct auxiliary_device *adev)
+{
+ struct yoga_c630_psy *ecbat = auxiliary_get_drvdata(adev);
+
+ yoga_c630_ec_unregister_notify(ecbat->ec, &ecbat->nb);
+ power_supply_unregister(ecbat->bat_psy);
+}
+
+static const struct auxiliary_device_id yoga_c630_psy_id_table[] = {
+ { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_PSY, },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, yoga_c630_psy_id_table);
+
+static struct auxiliary_driver yoga_c630_psy_driver = {
+ .name = YOGA_C630_DEV_PSY,
+ .id_table = yoga_c630_psy_id_table,
+ .probe = yoga_c630_psy_probe,
+ .remove = yoga_c630_psy_remove,
+};
+
+module_auxiliary_driver(yoga_c630_psy_driver);
+
+MODULE_DESCRIPTION("Lenovo Yoga C630 psy");
+MODULE_LICENSE("GPL");

--
2.39.2


2024-05-27 10:38:36

by Nikita Travkin

[permalink] [raw]
Subject: Re: [PATCH v3 1/6] dt-bindings: power: supply: Add Lenovo Yoga C630 EC

Dmitry Baryshkov писал(а) 27.05.2024 15:03:
> From: Bjorn Andersson <[email protected]>
>
> Add binding for the Embedded Controller found in the Qualcomm
> Snapdragon-based Lenovo Yoga C630.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> .../bindings/power/supply/lenovo,yoga-c630-ec.yaml | 83 ++++++++++++++++++++++
> 1 file changed, 83 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
> new file mode 100644
> index 000000000000..52a302850743
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
> @@ -0,0 +1,83 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/power/supply/lenovo,yoga-c630-ec.yaml#

Should this binding join aspire1 one in bindings/platform ?

Nikita

> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Lenovo Yoga C630 Embedded Controller.
> +
> +maintainers:
> + - Bjorn Andersson <[email protected]>
> +
> +description:
> + The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller
> + (EC) which handles things such as battery and USB Type-C. This binding
> + describes the interface, on an I2C bus, to this EC.
> +
> +properties:
> + compatible:
> + const: lenovo,yoga-c630-ec
> +
> + reg:
> + const: 0x70
> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 0
> +
> + interrupts:
> + maxItems: 1
> +
> +patternProperties:
> + '^connector@[01]$':
> + $ref: /schemas/connector/usb-connector.yaml#
> +
> + properties:
> + reg:
> + maxItems: 1
> +
> + unevaluatedProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> +
> +additionalProperties: false
> +
> +examples:
> + - |+
> + #include <dt-bindings/interrupt-controller/irq.h>
> + i2c1 {
> + clock-frequency = <400000>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + embedded-controller@70 {
> + compatible = "lenovo,yoga-c630-ec";
> + reg = <0x70>;
> +
> + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + connector@0 {
> + compatible = "usb-c-connector";
> + reg = <0>;
> + power-role = "source";
> + data-role = "host";
> + };
> +
> + connector@1 {
> + compatible = "usb-c-connector";
> + reg = <1>;
> + power-role = "source";
> + data-role = "host";
> + };
> + };
> + };
> +...

2024-05-27 11:04:30

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v3 1/6] dt-bindings: power: supply: Add Lenovo Yoga C630 EC

On Mon, May 27, 2024 at 03:21:27PM +0500, Nikita Travkin wrote:
> Dmitry Baryshkov писал(а) 27.05.2024 15:03:
> > From: Bjorn Andersson <[email protected]>
> >
> > Add binding for the Embedded Controller found in the Qualcomm
> > Snapdragon-based Lenovo Yoga C630.
> >
> > Signed-off-by: Bjorn Andersson <[email protected]>
> > Reviewed-by: Krzysztof Kozlowski <[email protected]>
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > .../bindings/power/supply/lenovo,yoga-c630-ec.yaml | 83 ++++++++++++++++++++++
> > 1 file changed, 83 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
> > new file mode 100644
> > index 000000000000..52a302850743
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/power/supply/lenovo,yoga-c630-ec.yaml
> > @@ -0,0 +1,83 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/power/supply/lenovo,yoga-c630-ec.yaml#
>
> Should this binding join aspire1 one in bindings/platform ?

Good idea

>
> Nikita
>
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Lenovo Yoga C630 Embedded Controller.
> > +
> > +maintainers:
> > + - Bjorn Andersson <[email protected]>
> > +
> > +description:
> > + The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller
> > + (EC) which handles things such as battery and USB Type-C. This binding
> > + describes the interface, on an I2C bus, to this EC.
> > +
> > +properties:
> > + compatible:
> > + const: lenovo,yoga-c630-ec
> > +
> > + reg:
> > + const: 0x70
> > +
> > + '#address-cells':
> > + const: 1
> > +
> > + '#size-cells':
> > + const: 0
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > +patternProperties:
> > + '^connector@[01]$':
> > + $ref: /schemas/connector/usb-connector.yaml#
> > +
> > + properties:
> > + reg:
> > + maxItems: 1
> > +
> > + unevaluatedProperties: false
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |+
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > + i2c1 {
> > + clock-frequency = <400000>;
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + embedded-controller@70 {
> > + compatible = "lenovo,yoga-c630-ec";
> > + reg = <0x70>;
> > +
> > + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + connector@0 {
> > + compatible = "usb-c-connector";
> > + reg = <0>;
> > + power-role = "source";
> > + data-role = "host";
> > + };
> > +
> > + connector@1 {
> > + compatible = "usb-c-connector";
> > + reg = <1>;
> > + power-role = "source";
> > + data-role = "host";
> > + };
> > + };
> > + };
> > +...

--
With best wishes
Dmitry

2024-05-27 12:26:56

by Oliver Neukum

[permalink] [raw]
Subject: Re: [PATCH v3 4/6] power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver

On 27.05.24 12:03, Dmitry Baryshkov wrote:

Hi,

> +struct yoga_c630_psy {
> + struct yoga_c630_ec *ec;
> + struct device *dev;
> + struct device_node *of_node;
> + struct notifier_block nb;
> + struct mutex lock;
> +
> + struct power_supply *adp_psy;
> + struct power_supply *bat_psy;
> +
> + unsigned long last_status_update;
> +
> + bool adapter_online;
> +
> + bool unit_mA;
> +
> + unsigned int scale;

why do you store unit_mA and scale? This looks redundant and like a source
of confusion to me.

Regards
Oliver

2024-05-27 23:16:18

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v3 4/6] power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver

On Mon, May 27, 2024 at 02:26:36PM +0200, Oliver Neukum wrote:
> On 27.05.24 12:03, Dmitry Baryshkov wrote:
>
> Hi,
>
> > +struct yoga_c630_psy {
> > + struct yoga_c630_ec *ec;
> > + struct device *dev;
> > + struct device_node *of_node;
> > + struct notifier_block nb;
> > + struct mutex lock;
> > +
> > + struct power_supply *adp_psy;
> > + struct power_supply *bat_psy;
> > +
> > + unsigned long last_status_update;
> > +
> > + bool adapter_online;
> > +
> > + bool unit_mA;
> > +
> > + unsigned int scale;
>
> why do you store unit_mA and scale? This looks redundant and like a source
> of confusion to me.

Here we just followed the AML code in ACPI tables. The unit_mA is a
returned from the_BIX method, the 'scale' is used internally in the DSDT.
If you think that it's better, I can change all '* scale * 1000' to
'if unit_mA then foo = bar * 10000 else foo = bar * 1000'.

--
With best wishes
Dmitry

2024-05-28 08:43:32

by Oliver Neukum

[permalink] [raw]
Subject: Re: [PATCH v3 4/6] power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver

On 28.05.24 01:15, Dmitry Baryshkov wrote:
> On Mon, May 27, 2024 at 02:26:36PM +0200, Oliver Neukum wrote:
>> On 27.05.24 12:03, Dmitry Baryshkov wrote:

Hi,

>>> +struct yoga_c630_psy {
>>> + struct yoga_c630_ec *ec;
>>> + struct device *dev;
>>> + struct device_node *of_node;
>>> + struct notifier_block nb;
>>> + struct mutex lock;
>>> +
>>> + struct power_supply *adp_psy;
>>> + struct power_supply *bat_psy;
>>> +
>>> + unsigned long last_status_update;
>>> +
>>> + bool adapter_online;
>>> +
>>> + bool unit_mA;
>>> +
>>> + unsigned int scale;
>>
>> why do you store unit_mA and scale? This looks redundant and like a source
>> of confusion to me.
>
> Here we just followed the AML code in ACPI tables. The unit_mA is a
> returned from the_BIX method, the 'scale' is used internally in the DSDT.
> If you think that it's better, I can change all '* scale * 1000' to
> 'if unit_mA then foo = bar * 10000 else foo = bar * 1000'.

I think that would indeed be better. Implementation details of the DSDT
should not dictate data structures in a kernel driver.

Regards
Oliver


2024-05-28 09:08:37

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v3 4/6] power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver

On Tue, 28 May 2024 at 11:43, Oliver Neukum <[email protected]> wrote:
>
> On 28.05.24 01:15, Dmitry Baryshkov wrote:
> > On Mon, May 27, 2024 at 02:26:36PM +0200, Oliver Neukum wrote:
> >> On 27.05.24 12:03, Dmitry Baryshkov wrote:
>
> Hi,
>
> >>> +struct yoga_c630_psy {
> >>> + struct yoga_c630_ec *ec;
> >>> + struct device *dev;
> >>> + struct device_node *of_node;
> >>> + struct notifier_block nb;
> >>> + struct mutex lock;
> >>> +
> >>> + struct power_supply *adp_psy;
> >>> + struct power_supply *bat_psy;
> >>> +
> >>> + unsigned long last_status_update;
> >>> +
> >>> + bool adapter_online;
> >>> +
> >>> + bool unit_mA;
> >>> +
> >>> + unsigned int scale;
> >>
> >> why do you store unit_mA and scale? This looks redundant and like a source
> >> of confusion to me.
> >
> > Here we just followed the AML code in ACPI tables. The unit_mA is a
> > returned from the_BIX method, the 'scale' is used internally in the DSDT.
> > If you think that it's better, I can change all '* scale * 1000' to
> > 'if unit_mA then foo = bar * 10000 else foo = bar * 1000'.
>
> I think that would indeed be better. Implementation details of the DSDT
> should not dictate data structures in a kernel driver.

Ack.

>
> Regards
> Oliver
>


--
With best wishes
Dmitry

2024-05-28 15:09:30

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v3 6/6] arm64: dts: qcom: c630: Add Embedded Controller node

On Mon, May 27, 2024 at 01:03:51PM GMT, Dmitry Baryshkov wrote:
> From: Bjorn Andersson <[email protected]>

Please align this with the S-o-b - feel free to use either form.

>
> The Embedded Controller in the Lenovo Yoga C630 is accessible on &i2c1
> and provides battery and adapter status, as well as altmode
> notifications for the second USB Type-C port.
>
> Add a definition for the EC.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> .../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++++++++++++++++++++
> 1 file changed, 76 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> index 47dc42f6e936..d975f78eb3ab 100644
> --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> @@ -370,6 +370,66 @@ zap-shader {
> &i2c1 {
> status = "okay";
> clock-frequency = <400000>;
> +
> + embedded-controller@70 {
> + compatible = "lenovo,yoga-c630-ec";
> + reg = <0x70>;
> +
> + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&ec_int_state>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + connector@0 {
> + compatible = "usb-c-connector";
> + reg = <0>;
> + power-role = "dual";
> + data-role = "host";

I was under the impression that this port is wired directly to the SoC
and as such this would support data role switching as well.

No concern with that, but just out of curiosity, is this not the case?

Regards,
Bjorn

2024-05-28 15:14:28

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH v3 6/6] arm64: dts: qcom: c630: Add Embedded Controller node

On Tue, 28 May 2024 at 18:06, Bjorn Andersson <[email protected]> wrote:
>
> On Mon, May 27, 2024 at 01:03:51PM GMT, Dmitry Baryshkov wrote:
> > From: Bjorn Andersson <[email protected]>
>
> Please align this with the S-o-b - feel free to use either form.

Ack. I'll check what went wrong.

>
> >
> > The Embedded Controller in the Lenovo Yoga C630 is accessible on &i2c1
> > and provides battery and adapter status, as well as altmode
> > notifications for the second USB Type-C port.
> >
> > Add a definition for the EC.
> >
> > Signed-off-by: Bjorn Andersson <[email protected]>
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > .../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++++++++++++++++++++
> > 1 file changed, 76 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > index 47dc42f6e936..d975f78eb3ab 100644
> > --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > @@ -370,6 +370,66 @@ zap-shader {
> > &i2c1 {
> > status = "okay";
> > clock-frequency = <400000>;
> > +
> > + embedded-controller@70 {
> > + compatible = "lenovo,yoga-c630-ec";
> > + reg = <0x70>;
> > +
> > + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
> > +
> > + pinctrl-names = "default";
> > + pinctrl-0 = <&ec_int_state>;
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + connector@0 {
> > + compatible = "usb-c-connector";
> > + reg = <0>;
> > + power-role = "dual";
> > + data-role = "host";
>
> I was under the impression that this port is wired directly to the SoC
> and as such this would support data role switching as well.
>
> No concern with that, but just out of curiosity, is this not the case?

It is wired through the external Type-C port controller RTS5437, which
also controls the vbus pins, etc. The UCSI firmware reports both ports
as host-only and doesn't implement data role switching. So, having it
as "host" is a safe bet.

I must admit, I also hoped to be able to use this port in gadget mode,
but it seems to be nearly impossible.


--
With best wishes
Dmitry

2024-05-28 17:14:16

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH v3 6/6] arm64: dts: qcom: c630: Add Embedded Controller node

On Tue, May 28, 2024 at 06:12:58PM GMT, Dmitry Baryshkov wrote:
> On Tue, 28 May 2024 at 18:06, Bjorn Andersson <[email protected]> wrote:
> >
> > On Mon, May 27, 2024 at 01:03:51PM GMT, Dmitry Baryshkov wrote:
> > > From: Bjorn Andersson <[email protected]>
> >
> > Please align this with the S-o-b - feel free to use either form.
>
> Ack. I'll check what went wrong.
>
> >
> > >
> > > The Embedded Controller in the Lenovo Yoga C630 is accessible on &i2c1
> > > and provides battery and adapter status, as well as altmode
> > > notifications for the second USB Type-C port.
> > >
> > > Add a definition for the EC.
> > >
> > > Signed-off-by: Bjorn Andersson <[email protected]>
> > > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > > ---
> > > .../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++++++++++++++++++++
> > > 1 file changed, 76 insertions(+)
> > >
> > > diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > > index 47dc42f6e936..d975f78eb3ab 100644
> > > --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > > +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> > > @@ -370,6 +370,66 @@ zap-shader {
> > > &i2c1 {
> > > status = "okay";
> > > clock-frequency = <400000>;
> > > +
> > > + embedded-controller@70 {
> > > + compatible = "lenovo,yoga-c630-ec";
> > > + reg = <0x70>;
> > > +
> > > + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
> > > +
> > > + pinctrl-names = "default";
> > > + pinctrl-0 = <&ec_int_state>;
> > > +
> > > + #address-cells = <1>;
> > > + #size-cells = <0>;
> > > +
> > > + connector@0 {
> > > + compatible = "usb-c-connector";
> > > + reg = <0>;
> > > + power-role = "dual";
> > > + data-role = "host";
> >
> > I was under the impression that this port is wired directly to the SoC
> > and as such this would support data role switching as well.
> >
> > No concern with that, but just out of curiosity, is this not the case?
>
> It is wired through the external Type-C port controller RTS5437, which
> also controls the vbus pins, etc. The UCSI firmware reports both ports
> as host-only and doesn't implement data role switching. So, having it
> as "host" is a safe bet.
>

Thanks for the explanation, that makes sense.

> I must admit, I also hoped to be able to use this port in gadget mode,
> but it seems to be nearly impossible.
>

Someone must have managed to use the device in peripheral mode to get
the firmware on there originally...just saying... ;)

Regards,
Bjorn

2024-05-28 17:42:41

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v3 0/6] power: supply: Lenovo Yoga C630 EC


On Mon, 27 May 2024 13:03:45 +0300, Dmitry Baryshkov wrote:
> This adds binding, driver and the DT support for the Lenovo Yoga C630
> Embedded Controller, to provide battery information.
>
> Support for this EC was implemented by Bjorn, who later could not work
> on this driver. I've picked this patchset up and updated it following
> the pending review comments.
>
> DisplayPort support is still not a part of this patchset. It uses EC
> messages to provide AltMode information rather than implementing
> corresponding UCSI commands. However to have a cleaner uAPI story, the
> AltMode should be handled via the same Type-C port.
>
> Merge strategy: the driver bits depend on the platform/arm64 patch,
> which adds interface for the subdrivers. I'd either ask to get that
> patch merged to the immutable branch, which then can be picked up by
> power/supply and USB trees or, to make life simpler, ack merging all
> driver bits e.g. through USB subsystem (I'm biased here since I plan to
> send more cleanups for the UCSI subsystem, which would otherwise result
> in cross-subsystem conflicts).
>
> ---
> Changes in v3:
> - Split the driver into core and power supply drivers,
> - Added UCSI driver part, handling USB connections,
> - Fixed Bjorn's address in DT bindings (Brian Masney)
> - Changed power-role for both ports to be "dual" per UCSI
> - Link to v2: https://lore.kernel.org/linux-arm-msm/[email protected]/
>
> Changes in v2:
> - Dropped DP support for now, as the bindings are in process of being
> discussed separately,
> - Merged dt patch into the same patchseries,
> - Removed the fixed serial number battery property,
> - Fixed indentation of dt bindings example,
> - Added property: reg and unevaluatedProperties to the connector
> bindings.
> - Link to v1: https://lore.kernel.org/linux-arm-msm/[email protected]/
>
> ---
> Bjorn Andersson (2):
> dt-bindings: power: supply: Add Lenovo Yoga C630 EC
> arm64: dts: qcom: c630: Add Embedded Controller node
>
> Dmitry Baryshkov (4):
> platform: arm64: add Lenovo Yoga C630 WOS EC driver
> usb: typec: ucsi: add Lenovo Yoga C630 glue driver
> power: supply: lenovo_yoga_c630_battery: add Lenovo C630 driver
> arm64: dts: qcom: sdm845: describe connections of USB/DP port
>
> .../bindings/power/supply/lenovo,yoga-c630-ec.yaml | 83 ++++
> arch/arm64/boot/dts/qcom/sdm845.dtsi | 53 ++-
> .../boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 76 ++++
> drivers/platform/arm64/Kconfig | 14 +
> drivers/platform/arm64/Makefile | 1 +
> drivers/platform/arm64/lenovo-yoga-c630.c | 279 ++++++++++++
> drivers/power/supply/Kconfig | 9 +
> drivers/power/supply/Makefile | 1 +
> drivers/power/supply/lenovo_yoga_c630_battery.c | 476 +++++++++++++++++++++
> drivers/usb/typec/ucsi/Kconfig | 9 +
> drivers/usb/typec/ucsi/Makefile | 1 +
> drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 189 ++++++++
> include/linux/platform_data/lenovo-yoga-c630.h | 42 ++
> 13 files changed, 1232 insertions(+), 1 deletion(-)
> ---
> base-commit: 8314289a8d50a4e05d8ece1ae0445a3b57bb4d3b
> change-id: 20240527-yoga-ec-driver-76fd7f5ddae8
>
> Best regards,
> --
> Dmitry Baryshkov <[email protected]>
>
>
>


My bot found new DTB warnings on the .dts files added or changed in this
series.

Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
are fixed by another series. Ultimately, it is up to the platform
maintainer whether these warnings are acceptable or not. No need to reply
unless the platform maintainer has comments.

If you already ran DT checks and didn't see these error(s), then
make sure dt-schema is up to date:

pip3 install dtschema --upgrade


New warnings running 'make CHECK_DTBS=y qcom/sdm850-lenovo-yoga-c630.dtb' for [email protected]:

arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dtb: pinctrl@3400000: ec-int-state: 'oneOf' conditional failed, one must be fixed:
'bias-disable', 'function', 'input-enable', 'pins' do not match any of the regexes: '-pins$', 'pinctrl-[0-9]+'
False schema does not allow True
from schema $id: http://devicetree.org/schemas/pinctrl/qcom,sdm845-pinctrl.yaml#