X86 platform drivers
 help / color / mirror / Atom feed
* [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev
@ 2024-11-04 20:35 Hans de Goede
  2024-11-04 20:35 ` [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver Hans de Goede
  2024-11-05  8:51 ` [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Andy Shevchenko
  0 siblings, 2 replies; 7+ messages in thread
From: Hans de Goede @ 2024-11-04 20:35 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel
  Cc: Hans de Goede, platform-driver-x86, linux-pm

On some Android tablets with Crystal Cove PMIC the DSDT lacks an ACPI AC
device to indicate whether a charger is plugged in or not.

Add support for registering a "crystal_cove_pwrsrc" power_supply class
device to indicate charger online status. This is made conditional on
a "linux,register-pwrsrc-power_supply" boolean device-property to avoid
registering a duplicate power_supply class device on devices where this
is already handled by an ACPI AC device.

Note the "linux,register-pwrsrc-power_supply" property is only used on
x86/ACPI (non devicetree) devs and the devicetree-bindings maintainers
have requested properties like these to not be added to the devicetree
bindings, so the new property is deliberately not added to any bindings.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/platform/x86/intel/bytcrc_pwrsrc.c | 76 +++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c
index 418b71af27ff..98b5057d4c68 100644
--- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c
+++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c
@@ -8,13 +8,21 @@
  * Copyright (C) 2013 Intel Corporation
  */
 
+#include <linux/bits.h>
 #include <linux/debugfs.h>
+#include <linux/interrupt.h>
 #include <linux/mfd/intel_soc_pmic.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
 #include <linux/regmap.h>
 
+#define CRYSTALCOVE_PWRSRC_IRQ		0x03
 #define CRYSTALCOVE_SPWRSRC_REG		0x1E
+#define CRYSTALCOVE_SPWRSRC_USB		BIT(0)
+#define CRYSTALCOVE_SPWRSRC_DC		BIT(1)
+#define CRYSTALCOVE_SPWRSRC_BATTERY	BIT(2)
 #define CRYSTALCOVE_RESETSRC0_REG	0x20
 #define CRYSTALCOVE_RESETSRC1_REG	0x21
 #define CRYSTALCOVE_WAKESRC_REG		0x22
@@ -22,6 +30,7 @@
 struct crc_pwrsrc_data {
 	struct regmap *regmap;
 	struct dentry *debug_dentry;
+	struct power_supply *psy;
 	unsigned int resetsrc0;
 	unsigned int resetsrc1;
 	unsigned int wakesrc;
@@ -118,11 +127,57 @@ static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data,
 	return regmap_write(data->regmap, reg, *val);
 }
 
+static irqreturn_t crc_pwrsrc_irq_handler(int irq, void *_data)
+{
+	struct crc_pwrsrc_data *data = _data;
+	unsigned int irq_mask;
+
+	if (regmap_read(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, &irq_mask))
+		return IRQ_NONE;
+
+	regmap_write(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, irq_mask);
+
+	power_supply_changed(data->psy);
+	return IRQ_HANDLED;
+}
+
+static int crc_pwrsrc_psy_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct crc_pwrsrc_data *data = power_supply_get_drvdata(psy);
+	unsigned int pwrsrc;
+	int ret;
+
+	if (psp != POWER_SUPPLY_PROP_ONLINE)
+		return -EINVAL;
+
+	ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &pwrsrc);
+	if (ret)
+		return ret;
+
+	val->intval = !!(pwrsrc & (CRYSTALCOVE_SPWRSRC_USB |
+				   CRYSTALCOVE_SPWRSRC_DC));
+	return 0;
+}
+
+static const enum power_supply_property crc_pwrsrc_psy_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc crc_pwrsrc_psy_desc = {
+	.name = "crystal_cove_pwrsrc",
+	.type = POWER_SUPPLY_TYPE_MAINS,
+	.properties = crc_pwrsrc_psy_props,
+	.num_properties = ARRAY_SIZE(crc_pwrsrc_psy_props),
+	.get_property = crc_pwrsrc_psy_get_property,
+};
+
 static int crc_pwrsrc_probe(struct platform_device *pdev)
 {
 	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
 	struct crc_pwrsrc_data *data;
-	int ret;
+	int irq, ret;
 
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -149,6 +204,25 @@ static int crc_pwrsrc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	if (device_property_read_bool(pdev->dev.parent, "linux,register-pwrsrc-power_supply")) {
+		struct power_supply_config psy_cfg = { .drv_data = data };
+
+		irq = platform_get_irq(pdev, 0);
+		if (irq < 0)
+			return dev_err_probe(&pdev->dev, irq, "getting IRQ\n");
+
+		data->psy = devm_power_supply_register(&pdev->dev, &crc_pwrsrc_psy_desc, &psy_cfg);
+		if (IS_ERR(data->psy))
+			return dev_err_probe(&pdev->dev, PTR_ERR(data->psy),
+					     "registering power-supply\n");
+
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+						crc_pwrsrc_irq_handler,
+						IRQF_ONESHOT, KBUILD_MODNAME, data);
+		if (ret)
+			return dev_err_probe(&pdev->dev, ret, "requesting IRQ\n");
+	}
+
 	data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL);
 	debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops);
 	debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops);
-- 
2.47.0


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

* [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver
  2024-11-04 20:35 [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Hans de Goede
@ 2024-11-04 20:35 ` Hans de Goede
  2024-11-05 10:39   ` Andy Shevchenko
  2024-11-05  8:51 ` [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Andy Shevchenko
  1 sibling, 1 reply; 7+ messages in thread
From: Hans de Goede @ 2024-11-04 20:35 UTC (permalink / raw)
  To: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel
  Cc: Hans de Goede, platform-driver-x86, linux-pm

The Vexia EDU ATLA 10 tablet has an embedded controller instead of
giving the os direct access to the charger + fuel-gauge ICs as is normal
on tablets designed for Android.

There is ACPI Battery device in the DSDT using the EC which should work
expect that it expects the I2C controller to be enumerated as an ACPI
device and the tablet's BIOS enumerates all LPSS devices as PCI devices
(and changing the LPSS BIOS settings from PCI -> ACPI does not work).

Add a power_supply class driver for the Atla 10 EC to expert battery info
to userspace. This is made part of the x86-android-tablets directory and
Kconfig option because the i2c_client it binds to is instantiated by
the x86-android-tablets kmod.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 .../platform/x86/x86-android-tablets/Makefile |   1 +
 .../x86/x86-android-tablets/vexia_atla10_ec.c | 264 ++++++++++++++++++
 2 files changed, 265 insertions(+)
 create mode 100644 drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c

diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile
index 41ece5a37137..bc505ffcd2bf 100644
--- a/drivers/platform/x86/x86-android-tablets/Makefile
+++ b/drivers/platform/x86/x86-android-tablets/Makefile
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
+obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o
 
 x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
 			 asus.o lenovo.o other.o
diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
new file mode 100644
index 000000000000..c5e6656d24fc
--- /dev/null
+++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * power_supply class (battery) driver for the I2C attached embedded controller
+ * found on Vexia EDU ATLA 10 (9V version) tablets.
+ *
+ * This is based on the ACPI Battery device in the DSDT which should work
+ * expect that it expects the I2C controller to be enumerated as an ACPI
+ * device and the tablet's BIOS enumerates all LPSS devices as PCI devices
+ * (and changing the LPSS BIOS settings from PCI -> ACPI does not work).
+ *
+ * Copyright (c) 2024 Hans de Goede <hansg@kernel.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/devm-helpers.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <asm/byteorder.h>
+
+/* State field uses ACPI Battery spec status bits */
+#define ACPI_BATTERY_STATE_DISCHARGING		BIT(0)
+#define ACPI_BATTERY_STATE_CHARGING		BIT(1)
+
+#define ATLA10_EC_BATTERY_STATE_COMMAND		0x87
+#define ATLA10_EC_BATTERY_INFO_COMMAND		0x88
+
+/* From broken ACPI battery device in DSDT */
+#define ATLA10_EC_VOLTAGE_MIN_DESIGN		3750000
+
+struct atla10_ec_battery_state {
+	u8 len;				/* Struct length excluding the len field, always 12 */
+	u8 status;			/* Using ACPI Battery spec status bits */
+	u8 capacity;			/* Percent */
+	__le16 charge_now;		/* mAh */
+	__le16 voltage_now;		/* mV */
+	__le16 current_now;		/* mA */
+	__le16 charge_full;		/* mAh */
+	__le16 temp;			/* centi degrees celcius */
+} __packed;
+
+struct atla10_ec_battery_info {
+	u8 len;				/* Struct length excluding the len field, always 6 */
+	__le16 charge_full_design;	/* mAh */
+	__le16 voltage_now;		/* mV, should be design voltage, but is not ? */
+	__le16 charge_full_design2;	/* mAh */
+} __packed;
+
+struct atla10_ec_data {
+	struct i2c_client *client;
+	struct power_supply *psy;
+	struct delayed_work work;
+	struct mutex update_lock;
+	struct atla10_ec_battery_info info;
+	struct atla10_ec_battery_state state;
+	bool valid;			/* true if state is valid */
+	unsigned long last_update;	/* In jiffies */
+};
+
+static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(data->client, cmd, len, values);
+	if (ret != len) {
+		dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret);
+		return -EIO;
+	}
+
+	if (values[0] != (len - 1)) {
+		dev_err(dev, "I2C command 0x%02x header length mismatch expected %u got %u\n",
+			cmd, len - 1, values[0]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int atla10_ec_update(struct atla10_ec_data *data)
+{
+	int ret;
+
+	/* Cache data for 5 seconds */
+	if (data->valid && time_before(jiffies, data->last_update + 5 * HZ))
+		return 0;
+
+	ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_STATE_COMMAND,
+			    sizeof(data->state), (u8 *)&data->state);
+	if (ret)
+		return ret;
+
+	data->last_update = jiffies;
+	data->valid = true;
+	return 0;
+}
+
+static int atla10_ec_psy_get_property(struct power_supply *psy,
+				      enum power_supply_property psp,
+				      union power_supply_propval *val)
+{
+	struct atla10_ec_data *data = power_supply_get_drvdata(psy);
+	int charge_now, charge_full, ret;
+
+	guard(mutex)(&data->update_lock);
+
+	ret = atla10_ec_update(data);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING)
+			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		else if (data->state.status & ACPI_BATTERY_STATE_CHARGING)
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+		else if (data->state.capacity == 100)
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+		else
+			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = data->state.capacity;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		/*
+		 * The EC has a bug where it reports charge-full-design as
+		 * charge-now when the battery is full. Clamp charge-now to
+		 * charge-full to workaround this.
+		 */
+		charge_now = le16_to_cpu(data->state.charge_now);
+		charge_full = le16_to_cpu(data->state.charge_full);
+		val->intval = min(charge_now, charge_full) * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = le16_to_cpu(data->state.voltage_now) * 1000;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = le16_to_cpu(data->state.current_now) * 1000;
+		/*
+		 * Documentation/ABI/testing/sysfs-class-power specifies
+		 * negative current for discharing.
+		 */
+		if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING)
+			val->intval = -val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = le16_to_cpu(data->state.charge_full) * 1000;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = le16_to_cpu(data->state.temp) / 10;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		val->intval = le16_to_cpu(data->info.charge_full_design) * 1000;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+		val->intval = ATLA10_EC_VOLTAGE_MIN_DESIGN;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void atla10_ec_external_power_changed_work(struct work_struct *work)
+{
+	struct atla10_ec_data *data = container_of(work, struct atla10_ec_data, work.work);
+
+	dev_dbg(&data->client->dev, "External power changed\n");
+	data->valid = false;
+	power_supply_changed(data->psy);
+}
+
+static void atla10_ec_external_power_changed(struct power_supply *psy)
+{
+	struct atla10_ec_data *data = power_supply_get_drvdata(psy);
+
+	/* After charger plug in/out wait 0.5s for things to stabilize */
+	mod_delayed_work(system_wq, &data->work, HZ / 2);
+}
+
+static const enum power_supply_property atla10_ec_psy_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static const struct power_supply_desc atla10_ec_psy_desc = {
+	.name = "atla10_ec_battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = atla10_ec_psy_props,
+	.num_properties = ARRAY_SIZE(atla10_ec_psy_props),
+	.get_property = atla10_ec_psy_get_property,
+	.external_power_changed = atla10_ec_external_power_changed,
+};
+
+static int atla10_ec_probe(struct i2c_client *client)
+{
+	struct power_supply_config psy_cfg = { };
+	struct device *dev = &client->dev;
+	struct atla10_ec_data *data;
+	int ret;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	psy_cfg.drv_data = data;
+	data->client = client;
+
+	ret = devm_mutex_init(dev, &data->update_lock);
+	if (ret)
+		return ret;
+
+	ret = devm_delayed_work_autocancel(dev, &data->work,
+					   atla10_ec_external_power_changed_work);
+	if (ret)
+		return ret;
+
+	ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_INFO_COMMAND,
+			    sizeof(data->info), (u8 *)&data->info);
+	if (ret)
+		return ret;
+
+	data->psy = devm_power_supply_register(dev, &atla10_ec_psy_desc, &psy_cfg);
+	return PTR_ERR_OR_ZERO(data->psy);
+}
+
+static const struct i2c_device_id atla10_ec_id_table[] = {
+	{ "vexia_atla10_ec" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table);
+
+static struct i2c_driver atla10_ec_driver = {
+	.driver = {
+		.name = "vexia_atla10_ec",
+	},
+	.probe = atla10_ec_probe,
+	.id_table = atla10_ec_id_table,
+};
+module_i2c_driver(atla10_ec_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC");
+MODULE_LICENSE("GPL");
-- 
2.47.0


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

* Re: [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev
  2024-11-04 20:35 [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Hans de Goede
  2024-11-04 20:35 ` [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver Hans de Goede
@ 2024-11-05  8:51 ` Andy Shevchenko
  2024-11-16 10:19   ` Hans de Goede
  1 sibling, 1 reply; 7+ messages in thread
From: Andy Shevchenko @ 2024-11-05  8:51 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel,
	platform-driver-x86, linux-pm

On Mon, Nov 4, 2024 at 10:36 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> On some Android tablets with Crystal Cove PMIC the DSDT lacks an ACPI AC
> device to indicate whether a charger is plugged in or not.
>
> Add support for registering a "crystal_cove_pwrsrc" power_supply class
> device to indicate charger online status. This is made conditional on
> a "linux,register-pwrsrc-power_supply" boolean device-property to avoid
> registering a duplicate power_supply class device on devices where this
> is already handled by an ACPI AC device.
>
> Note the "linux,register-pwrsrc-power_supply" property is only used on
> x86/ACPI (non devicetree) devs and the devicetree-bindings maintainers
> have requested properties like these to not be added to the devicetree
> bindings, so the new property is deliberately not added to any bindings.

Reviewed-by: Andy Shevchenko <andy@kernel.org>

...

+ array_size.h

> +#include <linux/bits.h>
>  #include <linux/debugfs.h>
> +#include <linux/interrupt.h>
>  #include <linux/mfd/intel_soc_pmic.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/property.h>
>  #include <linux/regmap.h>

...

> +       if (device_property_read_bool(pdev->dev.parent, "linux,register-pwrsrc-power_supply")) {

Btw, is that property type of boolean? If not,
device_property_present() has to be used.

...

> +               irq = platform_get_irq(pdev, 0);
> +               if (irq < 0)
> +                       return dev_err_probe(&pdev->dev, irq, "getting IRQ\n");

This dups the embedded error message.

...

> +               data->psy = devm_power_supply_register(&pdev->dev, &crc_pwrsrc_psy_desc, &psy_cfg);
> +               if (IS_ERR(data->psy))
> +                       return dev_err_probe(&pdev->dev, PTR_ERR(data->psy),
> +                                            "registering power-supply\n");
> +
> +               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> +                                               crc_pwrsrc_irq_handler,
> +                                               IRQF_ONESHOT, KBUILD_MODNAME, data);
> +               if (ret)
> +                       return dev_err_probe(&pdev->dev, ret, "requesting IRQ\n");

With

    struct device *dev = &pdev->dev;

at the top of the function you may make lines shorten and neater.

> +       }

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver
  2024-11-04 20:35 ` [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver Hans de Goede
@ 2024-11-05 10:39   ` Andy Shevchenko
  2024-11-13 18:39     ` Hans de Goede
  2024-11-16 10:43     ` Hans de Goede
  0 siblings, 2 replies; 7+ messages in thread
From: Andy Shevchenko @ 2024-11-05 10:39 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel,
	platform-driver-x86, linux-pm

On Mon, Nov 4, 2024 at 10:36 PM Hans de Goede <hdegoede@redhat.com> wrote:
>
> The Vexia EDU ATLA 10 tablet has an embedded controller instead of
> giving the os direct access to the charger + fuel-gauge ICs as is normal
> on tablets designed for Android.
>
> There is ACPI Battery device in the DSDT using the EC which should work
> expect that it expects the I2C controller to be enumerated as an ACPI

expect --> except

> device and the tablet's BIOS enumerates all LPSS devices as PCI devices
> (and changing the LPSS BIOS settings from PCI -> ACPI does not work).
>
> Add a power_supply class driver for the Atla 10 EC to expert battery info
> to userspace. This is made part of the x86-android-tablets directory and
> Kconfig option because the i2c_client it binds to is instantiated by
> the x86-android-tablets kmod.

Reviewed-by: Andy Shevchenko <andy@kernel.org>

...

>  obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
> +obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o

This splits the original (compound) object lines, please move it
either before (and this seems even better with ordering by name in
mind) or after this block.
>

Actually this blank line gives the false impression that the
originally two lines are not related. I would drop this blank line as
well.

>  x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
>                          asus.o lenovo.o other.o

...

> +#include <linux/bits.h>
> +#include <linux/devm-helpers.h>

+ err.h

> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/power_supply.h>
> +#include <linux/types.h>
> +#include <linux/workqueue.h>
> +
> +#include <asm/byteorder.h>

...

> +/* From broken ACPI battery device in DSDT */
> +#define ATLA10_EC_VOLTAGE_MIN_DESIGN           3750000

_uV ?

...

> +struct atla10_ec_battery_state {
> +       u8 len;                         /* Struct length excluding the len field, always 12 */
> +       u8 status;                      /* Using ACPI Battery spec status bits */
> +       u8 capacity;                    /* Percent */
> +       __le16 charge_now;              /* mAh */
> +       __le16 voltage_now;             /* mV */
> +       __le16 current_now;             /* mA */
> +       __le16 charge_full;             /* mAh */
> +       __le16 temp;                    /* centi degrees celcius */

Celsius / celsius

> +} __packed;
> +
> +struct atla10_ec_battery_info {
> +       u8 len;                         /* Struct length excluding the len field, always 6 */
> +       __le16 charge_full_design;      /* mAh */
> +       __le16 voltage_now;             /* mV, should be design voltage, but is not ? */
> +       __le16 charge_full_design2;     /* mAh */
> +} __packed;

Instead I would add the respective units to the variable names:
_mAh
_mV
...etc.

(* yes, with the capital letters to follow the proper spelling)

...

> +static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values)
> +{
> +       struct device *dev = &data->client->dev;
> +       int ret;
> +
> +       ret = i2c_smbus_read_i2c_block_data(data->client, cmd, len, values);
> +       if (ret != len) {
> +               dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret);
> +               return -EIO;
> +       }

> +       if (values[0] != (len - 1)) {

Hmm... AFAIU this is part of SMBus protocol. Why do we need to care
about this? Or is this an additional header on top of that?

> +               dev_err(dev, "I2C command 0x%02x header length mismatch expected %u got %u\n",
> +                       cmd, len - 1, values[0]);
> +               return -EIO;
> +       }
> +
> +       return 0;
> +}

...

> +               val->intval = min(charge_now, charge_full) * 1000;

MILLI (here and below)?

> +               break;
> +       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +               val->intval = le16_to_cpu(data->state.voltage_now) * 1000;
> +               break;
> +       case POWER_SUPPLY_PROP_CURRENT_NOW:
> +               val->intval = le16_to_cpu(data->state.current_now) * 1000;
> +               /*
> +                * Documentation/ABI/testing/sysfs-class-power specifies
> +                * negative current for discharing.

discharging

> +                */

--
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver
  2024-11-05 10:39   ` Andy Shevchenko
@ 2024-11-13 18:39     ` Hans de Goede
  2024-11-16 10:43     ` Hans de Goede
  1 sibling, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2024-11-13 18:39 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel,
	platform-driver-x86, linux-pm

Hi Andy,

On 5-Nov-24 11:39 AM, Andy Shevchenko wrote:
> On Mon, Nov 4, 2024 at 10:36 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> The Vexia EDU ATLA 10 tablet has an embedded controller instead of
>> giving the os direct access to the charger + fuel-gauge ICs as is normal
>> on tablets designed for Android.
>>
>> There is ACPI Battery device in the DSDT using the EC which should work
>> expect that it expects the I2C controller to be enumerated as an ACPI
> 
> expect --> except
> 
>> device and the tablet's BIOS enumerates all LPSS devices as PCI devices
>> (and changing the LPSS BIOS settings from PCI -> ACPI does not work).
>>
>> Add a power_supply class driver for the Atla 10 EC to expert battery info
>> to userspace. This is made part of the x86-android-tablets directory and
>> Kconfig option because the i2c_client it binds to is instantiated by
>> the x86-android-tablets kmod.
> 
> Reviewed-by: Andy Shevchenko <andy@kernel.org>

Thank you for the reviews for both patches. I'll try to prepare a v2
series addressing the small remarks you had tomorrow.

Regards,

Hans




> 
> ...
> 
>>  obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
>> +obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o
> 
> This splits the original (compound) object lines, please move it
> either before (and this seems even better with ordering by name in
> mind) or after this block.
>>
> 
> Actually this blank line gives the false impression that the
> originally two lines are not related. I would drop this blank line as
> well.
> 
>>  x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
>>                          asus.o lenovo.o other.o
> 
> ...
> 
>> +#include <linux/bits.h>
>> +#include <linux/devm-helpers.h>
> 
> + err.h
> 
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/power_supply.h>
>> +#include <linux/types.h>
>> +#include <linux/workqueue.h>
>> +
>> +#include <asm/byteorder.h>
> 
> ...
> 
>> +/* From broken ACPI battery device in DSDT */
>> +#define ATLA10_EC_VOLTAGE_MIN_DESIGN           3750000
> 
> _uV ?
> 
> ...
> 
>> +struct atla10_ec_battery_state {
>> +       u8 len;                         /* Struct length excluding the len field, always 12 */
>> +       u8 status;                      /* Using ACPI Battery spec status bits */
>> +       u8 capacity;                    /* Percent */
>> +       __le16 charge_now;              /* mAh */
>> +       __le16 voltage_now;             /* mV */
>> +       __le16 current_now;             /* mA */
>> +       __le16 charge_full;             /* mAh */
>> +       __le16 temp;                    /* centi degrees celcius */
> 
> Celsius / celsius
> 
>> +} __packed;
>> +
>> +struct atla10_ec_battery_info {
>> +       u8 len;                         /* Struct length excluding the len field, always 6 */
>> +       __le16 charge_full_design;      /* mAh */
>> +       __le16 voltage_now;             /* mV, should be design voltage, but is not ? */
>> +       __le16 charge_full_design2;     /* mAh */
>> +} __packed;
> 
> Instead I would add the respective units to the variable names:
> _mAh
> _mV
> ...etc.
> 
> (* yes, with the capital letters to follow the proper spelling)
> 
> ...
> 
>> +static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values)
>> +{
>> +       struct device *dev = &data->client->dev;
>> +       int ret;
>> +
>> +       ret = i2c_smbus_read_i2c_block_data(data->client, cmd, len, values);
>> +       if (ret != len) {
>> +               dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret);
>> +               return -EIO;
>> +       }
> 
>> +       if (values[0] != (len - 1)) {
> 
> Hmm... AFAIU this is part of SMBus protocol. Why do we need to care
> about this? Or is this an additional header on top of that?
> 
>> +               dev_err(dev, "I2C command 0x%02x header length mismatch expected %u got %u\n",
>> +                       cmd, len - 1, values[0]);
>> +               return -EIO;
>> +       }
>> +
>> +       return 0;
>> +}
> 
> ...
> 
>> +               val->intval = min(charge_now, charge_full) * 1000;
> 
> MILLI (here and below)?
> 
>> +               break;
>> +       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
>> +               val->intval = le16_to_cpu(data->state.voltage_now) * 1000;
>> +               break;
>> +       case POWER_SUPPLY_PROP_CURRENT_NOW:
>> +               val->intval = le16_to_cpu(data->state.current_now) * 1000;
>> +               /*
>> +                * Documentation/ABI/testing/sysfs-class-power specifies
>> +                * negative current for discharing.
> 
> discharging
> 
>> +                */
> 
> --
> With Best Regards,
> Andy Shevchenko
> 


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

* Re: [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev
  2024-11-05  8:51 ` [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Andy Shevchenko
@ 2024-11-16 10:19   ` Hans de Goede
  0 siblings, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2024-11-16 10:19 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel,
	platform-driver-x86, linux-pm

Hi,

On 5-Nov-24 9:51 AM, Andy Shevchenko wrote:
> On Mon, Nov 4, 2024 at 10:36 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> On some Android tablets with Crystal Cove PMIC the DSDT lacks an ACPI AC
>> device to indicate whether a charger is plugged in or not.
>>
>> Add support for registering a "crystal_cove_pwrsrc" power_supply class
>> device to indicate charger online status. This is made conditional on
>> a "linux,register-pwrsrc-power_supply" boolean device-property to avoid
>> registering a duplicate power_supply class device on devices where this
>> is already handled by an ACPI AC device.
>>
>> Note the "linux,register-pwrsrc-power_supply" property is only used on
>> x86/ACPI (non devicetree) devs and the devicetree-bindings maintainers
>> have requested properties like these to not be added to the devicetree
>> bindings, so the new property is deliberately not added to any bindings.
> 
> Reviewed-by: Andy Shevchenko <andy@kernel.org>
> 
> ...
> 
> + array_size.h
> 
>> +#include <linux/bits.h>
>>  #include <linux/debugfs.h>
>> +#include <linux/interrupt.h>
>>  #include <linux/mfd/intel_soc_pmic.h>
>>  #include <linux/module.h>
>>  #include <linux/platform_device.h>
>> +#include <linux/power_supply.h>
>> +#include <linux/property.h>
>>  #include <linux/regmap.h>
> 
> ...
> 
>> +       if (device_property_read_bool(pdev->dev.parent, "linux,register-pwrsrc-power_supply")) {
> 
> Btw, is that property type of boolean? If not,
> device_property_present() has to be used.

Yes this is a boolean property, as for your other remarks I agree
and I'll address them for the upcoming v2.

Regards,

Hans



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

* Re: [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver
  2024-11-05 10:39   ` Andy Shevchenko
  2024-11-13 18:39     ` Hans de Goede
@ 2024-11-16 10:43     ` Hans de Goede
  1 sibling, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2024-11-16 10:43 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Ilpo Järvinen, Andy Shevchenko, Sebastian Reichel,
	platform-driver-x86, linux-pm

Hi,

Thank you for the reviews.

On 5-Nov-24 11:39 AM, Andy Shevchenko wrote:
> On Mon, Nov 4, 2024 at 10:36 PM Hans de Goede <hdegoede@redhat.com> wrote:
>>
>> The Vexia EDU ATLA 10 tablet has an embedded controller instead of
>> giving the os direct access to the charger + fuel-gauge ICs as is normal
>> on tablets designed for Android.
>>
>> There is ACPI Battery device in the DSDT using the EC which should work
>> expect that it expects the I2C controller to be enumerated as an ACPI
> 
> expect --> except
> 
>> device and the tablet's BIOS enumerates all LPSS devices as PCI devices
>> (and changing the LPSS BIOS settings from PCI -> ACPI does not work).
>>
>> Add a power_supply class driver for the Atla 10 EC to expert battery info
>> to userspace. This is made part of the x86-android-tablets directory and
>> Kconfig option because the i2c_client it binds to is instantiated by
>> the x86-android-tablets kmod.
> 
> Reviewed-by: Andy Shevchenko <andy@kernel.org>
> 
> ...
> 
>>  obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
>> +obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o
> 
> This splits the original (compound) object lines, please move it
> either before (and this seems even better with ordering by name in
> mind) or after this block.
>>
> 
> Actually this blank line gives the false impression that the
> originally two lines are not related. I would drop this blank line as
> well.

Ack, both fixed for v2.

>>  x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
>>                          asus.o lenovo.o other.o
> 
> ...
> 
>> +#include <linux/bits.h>
>> +#include <linux/devm-helpers.h>
> 
> + err.h
> 
>> +#include <linux/i2c.h>
>> +#include <linux/module.h>
>> +#include <linux/power_supply.h>
>> +#include <linux/types.h>
>> +#include <linux/workqueue.h>
>> +
>> +#include <asm/byteorder.h>
> 
> ...
> 
>> +/* From broken ACPI battery device in DSDT */
>> +#define ATLA10_EC_VOLTAGE_MIN_DESIGN           3750000
> 
> _uV ?
> 
> ...
> 
>> +struct atla10_ec_battery_state {
>> +       u8 len;                         /* Struct length excluding the len field, always 12 */
>> +       u8 status;                      /* Using ACPI Battery spec status bits */
>> +       u8 capacity;                    /* Percent */
>> +       __le16 charge_now;              /* mAh */
>> +       __le16 voltage_now;             /* mV */
>> +       __le16 current_now;             /* mA */
>> +       __le16 charge_full;             /* mAh */
>> +       __le16 temp;                    /* centi degrees celcius */
> 
> Celsius / celsius
> 
>> +} __packed;
>> +
>> +struct atla10_ec_battery_info {
>> +       u8 len;                         /* Struct length excluding the len field, always 6 */
>> +       __le16 charge_full_design;      /* mAh */
>> +       __le16 voltage_now;             /* mV, should be design voltage, but is not ? */
>> +       __le16 charge_full_design2;     /* mAh */
>> +} __packed;
> 
> Instead I would add the respective units to the variable names:
> _mAh
> _mV
> ...etc.
> 
> (* yes, with the capital letters to follow the proper spelling)

Done for v2.

> ...
> 
>> +static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values)
>> +{
>> +       struct device *dev = &data->client->dev;
>> +       int ret;
>> +
>> +       ret = i2c_smbus_read_i2c_block_data(data->client, cmd, len, values);
>> +       if (ret != len) {
>> +               dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret);
>> +               return -EIO;
>> +       }
> 
>> +       if (values[0] != (len - 1)) {
> 
> Hmm... AFAIU this is part of SMBus protocol. Why do we need to care
> about this? Or is this an additional header on top of that?

Good point, I can indeed switch to i2c_smbus_read_block_data()
having it interpret the first read byte as len and drop the len field
from the struct definitions.

Done for v2.

>> +               dev_err(dev, "I2C command 0x%02x header length mismatch expected %u got %u\n",
>> +                       cmd, len - 1, values[0]);
>> +               return -EIO;
>> +       }
>> +
>> +       return 0;
>> +}
> 
> ...
> 
>> +               val->intval = min(charge_now, charge_full) * 1000;
> 
> MILLI (here and below)?

But this is not about converting from millis this is about going
from milles to micros.

Writing MILLI there is multiplying millis by milles_per_1 which
feels wrong. Now if there was a MILLIS_PER_MICRO that would make sense.

So I have kept this as is.

>> +               break;
>> +       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
>> +               val->intval = le16_to_cpu(data->state.voltage_now) * 1000;
>> +               break;
>> +       case POWER_SUPPLY_PROP_CURRENT_NOW:
>> +               val->intval = le16_to_cpu(data->state.current_now) * 1000;
>> +               /*
>> +                * Documentation/ABI/testing/sysfs-class-power specifies
>> +                * negative current for discharing.
> 
> discharging

Ack.

Regards,

Hans




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

end of thread, other threads:[~2024-11-16 10:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-04 20:35 [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Hans de Goede
2024-11-04 20:35 ` [PATCH 2/2] platform/x86: x86-android-tablets: Add Vexia EDU ATLA 10 EC battery driver Hans de Goede
2024-11-05 10:39   ` Andy Shevchenko
2024-11-13 18:39     ` Hans de Goede
2024-11-16 10:43     ` Hans de Goede
2024-11-05  8:51 ` [PATCH 1/2] platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev Andy Shevchenko
2024-11-16 10:19   ` Hans de Goede

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox