linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500
@ 2020-11-04 20:33 Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Dmitry Osipenko
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-04 20:33 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Thierry Reding, Jonathan Hunter,
	Dan Murphy, Sebastian Reichel
  Cc: devicetree, linux-tegra, linux-pm, linux-kernel

Hello!

This series adds support for the Embedded Controller which is found on
Acer Iconia Tab A500 (Android tablet device).

The Embedded Controller is ENE KB930 and it's running firmware customized
for the A500. The firmware interface may be reused by some other sibling
Acer tablets, although none of those tablets are supported in upstream yet.

Changelog:

v5: - No changes. Re-sending again in order to check whether dtschema-bot
      warning is resolved now, which didn't happen in v4 because bot used
      older 5.9 kernel code base instead of 5.10.

v4: - No code changes. Added r-b from Rob Herring and Sebastian Reichel.
      Re-sending for 5.11.

    - The v3 of LED driver was applied by Pavel Machek and already presents
      in v5.10 kernel.

v3: - Rebased on a recent linux-next. Fixed new merge conflict and dropped
      "regmap: Use flexible sleep" patch because it's already applied.

v2: - Factored out KB930 device-tree binding into a separate file, like it
      was suggested by Lubomir Rintel.

    - Switched to use regmap API like it was suggested by Lubomir Rintel.

    - Added patch "regmap: Use flexible sleep" which allows not to hog
      CPU while LED is switching state.

    - Corrected MODULE_LICENSE to use "GPL" in all patches.

    - Corrected MFD driver Kconfig entry like it was suggested by
      Lubomir Rintel, it now depends on I2C.

    - Switched to use I2C probe_new() in the MFD driver.

    - Renamed the global pm_off variable, like it was suggested by
      Lubomir Rintel and Lee Jones.

    - Dropped serial number from the battery driver because I realized
      that it's not a battery serial, but a device serial.

    - Battery driver now uses dev_err_probe(), like it was suggested by
      Sebastian Reichel.

    - Dropped legacy LED_ON usage from the LED driver and renamed the
      LEDs, like it was suggested by Pavel Machek. I also checked whether
      LED-name customization via device-tree could be needed by other
      potentially compatible devices and it shouldn't be needed, anyways it
      won't be difficult to extend the code even if I'm wrong.


Dmitry Osipenko (4):
  dt-bindings: mfd: Add ENE KB930 Embedded Controller binding
  mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  power: supply: Add battery gauge driver for Acer Iconia Tab A500
  ARM: tegra: acer-a500: Add Embedded Controller

 .../devicetree/bindings/mfd/ene-kb930.yaml    |  66 ++++
 .../boot/dts/tegra20-acer-a500-picasso.dts    |  17 +
 drivers/mfd/Kconfig                           |  12 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/acer-ec-a500.c                    | 203 ++++++++++++
 drivers/power/supply/Kconfig                  |   6 +
 drivers/power/supply/Makefile                 |   1 +
 drivers/power/supply/acer_a500_battery.c      | 297 ++++++++++++++++++
 8 files changed, 603 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/ene-kb930.yaml
 create mode 100644 drivers/mfd/acer-ec-a500.c
 create mode 100644 drivers/power/supply/acer_a500_battery.c

-- 
2.27.0


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

* [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding
  2020-11-04 20:33 [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500 Dmitry Osipenko
@ 2020-11-04 20:34 ` Dmitry Osipenko
  2020-11-06 16:11   ` Rob Herring
  2020-11-04 20:34 ` [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500 Dmitry Osipenko
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-04 20:34 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Thierry Reding, Jonathan Hunter,
	Dan Murphy, Sebastian Reichel
  Cc: devicetree, linux-tegra, linux-pm, linux-kernel

Add binding document for the ENE KB930 Embedded Controller.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../devicetree/bindings/mfd/ene-kb930.yaml    | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/ene-kb930.yaml

diff --git a/Documentation/devicetree/bindings/mfd/ene-kb930.yaml b/Documentation/devicetree/bindings/mfd/ene-kb930.yaml
new file mode 100644
index 000000000000..635c8966ca22
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ene-kb930.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/ene-kb930.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ENE KB930 Embedded Controller bindings
+
+description: |
+  This binding describes the ENE KB930 Embedded Controller attached to an
+  I2C bus.
+
+maintainers:
+  - Dmitry Osipenko <digetx@gmail.com>
+
+properties:
+  compatible:
+    items:
+      - enum:
+        - acer,a500-iconia-ec # Acer A500 Iconia tablet device
+      - enum:
+        - ene,kb930
+  reg:
+    maxItems: 1
+
+  monitored-battery: true
+  power-supplies: true
+  system-power-controller: true
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    battery: battery-cell {
+      compatible = "simple-battery";
+      charge-full-design-microamp-hours = <3260000>;
+      energy-full-design-microwatt-hours = <24000000>;
+      operating-range-celsius = <0 40>;
+    };
+
+    mains: ac-adapter {
+      compatible = "gpio-charger";
+      charger-type = "mains";
+      gpios = <&gpio 125 0>;
+    };
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      embedded-controller@58 {
+        compatible = "acer,a500-iconia-ec", "ene,kb930";
+        reg = <0x58>;
+
+        system-power-controller;
+
+        monitored-battery = <&battery>;
+        power-supplies = <&mains>;
+      };
+    };
+
+...
-- 
2.27.0


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

* [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-04 20:33 [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500 Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Dmitry Osipenko
@ 2020-11-04 20:34 ` Dmitry Osipenko
  2020-11-13  9:37   ` Lee Jones
  2020-11-04 20:34 ` [PATCH v5 3/4] power: supply: Add battery gauge driver for " Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 4/4] ARM: tegra: acer-a500: Add Embedded Controller Dmitry Osipenko
  3 siblings, 1 reply; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-04 20:34 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Thierry Reding, Jonathan Hunter,
	Dan Murphy, Sebastian Reichel
  Cc: devicetree, linux-tegra, linux-pm, linux-kernel

Acer Iconia Tab A500 is an Android tablet device, it has ENE KB930
Embedded Controller which provides battery-gauge, LED, GPIO and some
other functions. The EC uses firmware that is specifically customized
for Acer A500. This patch adds MFD driver for the Embedded Controller
which allows to power-off / reboot the A500 device, it also provides
a common register read/write API that will be used by the sub-devices.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/mfd/Kconfig        |  12 +++
 drivers/mfd/Makefile       |   1 +
 drivers/mfd/acer-ec-a500.c | 203 +++++++++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+)
 create mode 100644 drivers/mfd/acer-ec-a500.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8b99a13669bf..527ba5054d80 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2097,6 +2097,18 @@ config MFD_KHADAS_MCU
 	  additional drivers must be enabled in order to use the functionality
 	  of the device.
 
+config MFD_ACER_A500_EC
+	tristate "Embedded Controller driver for Acer Iconia Tab A500"
+	depends on I2C
+	depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP
+	help
+	  Support for Acer Iconia Tab A500 Embedded Controller.
+
+	  The controller itself is ENE KB930, it is running firmware
+	  customized for the specific needs of the Acer A500 hardware.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 1780019d2474..7bfc57c8b363 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828)	+= rohm-bd71828.o
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
+obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
 
 obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
 obj-$(CONFIG_MFD_SIMPLE_MFD_I2C)	+= simple-mfd-i2c.o
diff --git a/drivers/mfd/acer-ec-a500.c b/drivers/mfd/acer-ec-a500.c
new file mode 100644
index 000000000000..2785a6d9bcc4
--- /dev/null
+++ b/drivers/mfd/acer-ec-a500.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MFD driver for Acer Iconia Tab A500 Embedded Controller.
+ *
+ * Copyright 2020 GRATE-driver project.
+ *
+ * Based on downstream driver from Acer Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reboot.h>
+
+#define A500_EC_I2C_ERR_TIMEOUT		500
+#define A500_EC_POWER_CMD_TIMEOUT	1000
+
+enum {
+	REG_CURRENT_NOW = 0x03,
+	REG_SHUTDOWN = 0x52,
+	REG_WARM_REBOOT = 0x54,
+	REG_COLD_REBOOT = 0x55,
+};
+
+static struct i2c_client *a500_ec_client_pm_off;
+
+static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
+			void *val_buf, size_t val_sizel)
+{
+	struct i2c_client *client = context;
+	unsigned int reg, retries = 5;
+	u16 *ret_val = val_buf;
+	s32 ret = 0;
+
+	if (reg_size != 1 || val_sizel != 2)
+		return -EOPNOTSUPP;
+
+	reg = *(u8 *)reg_buf;
+
+	while (retries-- > 0) {
+		ret = i2c_smbus_read_word_data(client, reg);
+		if (ret >= 0)
+			break;
+
+		msleep(A500_EC_I2C_ERR_TIMEOUT);
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
+		return ret;
+	}
+
+	*ret_val = ret;
+
+	if (reg == REG_CURRENT_NOW)
+		fsleep(10000);
+
+	return 0;
+}
+
+static int a500_ec_write(void *context, const void *data, size_t count)
+{
+	struct i2c_client *client = context;
+	unsigned int reg, val, retries = 5;
+	s32 ret = 0;
+
+	if (count != 3)
+		return -EOPNOTSUPP;
+
+	reg = *(u8  *)(data + 0);
+	val = *(u16 *)(data + 1);
+
+	while (retries-- > 0) {
+		ret = i2c_smbus_write_word_data(client, reg, val);
+		if (ret >= 0)
+			break;
+
+		msleep(A500_EC_I2C_ERR_TIMEOUT);
+	}
+
+	if (ret < 0) {
+		dev_err(&client->dev, "write 0x%x failed: %d\n", reg, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config a500_ec_regmap_config = {
+	.name = "KB930",
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = 0xff,
+};
+
+static const struct regmap_bus a500_ec_regmap_bus = {
+	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+	.write = a500_ec_write,
+	.read = a500_ec_read,
+	.max_raw_read = 2,
+};
+
+static void a500_ec_poweroff(void)
+{
+	i2c_smbus_write_word_data(a500_ec_client_pm_off, REG_SHUTDOWN, 0);
+
+	mdelay(A500_EC_POWER_CMD_TIMEOUT);
+}
+
+static int a500_ec_restart_notify(struct notifier_block *this,
+				  unsigned long reboot_mode, void *data)
+{
+	if (reboot_mode == REBOOT_WARM)
+		i2c_smbus_write_word_data(a500_ec_client_pm_off,
+					  REG_WARM_REBOOT, 0);
+	else
+		i2c_smbus_write_word_data(a500_ec_client_pm_off,
+					  REG_COLD_REBOOT, 1);
+
+	mdelay(A500_EC_POWER_CMD_TIMEOUT);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block a500_ec_restart_handler = {
+	.notifier_call = a500_ec_restart_notify,
+	.priority = 200,
+};
+
+static const struct mfd_cell a500_ec_cells[] = {
+	{ .name = "acer-a500-iconia-battery", },
+	{ .name = "acer-a500-iconia-leds", },
+};
+
+static int a500_ec_probe(struct i2c_client *client)
+{
+	struct regmap *rmap;
+	int err;
+
+	rmap = devm_regmap_init(&client->dev, &a500_ec_regmap_bus,
+				client, &a500_ec_regmap_config);
+	if (IS_ERR(rmap))
+		return PTR_ERR(rmap);
+
+	/* register battery and LED sub-devices */
+	err = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
+				   a500_ec_cells, ARRAY_SIZE(a500_ec_cells),
+				   NULL, 0, NULL);
+	if (err) {
+		dev_err(&client->dev, "failed to add sub-devices: %d\n", err);
+		return err;
+	}
+
+	/* set up power management functions */
+	if (of_device_is_system_power_controller(client->dev.of_node)) {
+		a500_ec_client_pm_off = client;
+
+		err = register_restart_handler(&a500_ec_restart_handler);
+		if (err)
+			return err;
+
+		if (!pm_power_off)
+			pm_power_off = a500_ec_poweroff;
+	}
+
+	return 0;
+}
+
+static int a500_ec_remove(struct i2c_client *client)
+{
+	if (of_device_is_system_power_controller(client->dev.of_node)) {
+		if (pm_power_off == a500_ec_poweroff)
+			pm_power_off = NULL;
+
+		unregister_restart_handler(&a500_ec_restart_handler);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id a500_ec_match[] = {
+	{ .compatible = "acer,a500-iconia-ec" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, a500_ec_match);
+
+static struct i2c_driver a500_ec_driver = {
+	.driver = {
+		.name = "acer-a500-embedded-controller",
+		.of_match_table = a500_ec_match,
+	},
+	.probe_new = a500_ec_probe,
+	.remove = a500_ec_remove,
+};
+module_i2c_driver(a500_ec_driver);
+
+MODULE_DESCRIPTION("Acer Iconia Tab A500 Embedded Controller driver");
+MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
+MODULE_LICENSE("GPL");
-- 
2.27.0


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

* [PATCH v5 3/4] power: supply: Add battery gauge driver for Acer Iconia Tab A500
  2020-11-04 20:33 [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500 Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500 Dmitry Osipenko
@ 2020-11-04 20:34 ` Dmitry Osipenko
  2020-11-04 20:34 ` [PATCH v5 4/4] ARM: tegra: acer-a500: Add Embedded Controller Dmitry Osipenko
  3 siblings, 0 replies; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-04 20:34 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Thierry Reding, Jonathan Hunter,
	Dan Murphy, Sebastian Reichel
  Cc: devicetree, linux-tegra, linux-pm, linux-kernel

This patch adds battery gauge driver for Acer Iconia Tab A500 device.
The battery gauge function is provided via the Embedded Controller,
which is found on the Acer A500.

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/power/supply/Kconfig             |   6 +
 drivers/power/supply/Makefile            |   1 +
 drivers/power/supply/acer_a500_battery.c | 297 +++++++++++++++++++++++
 3 files changed, 304 insertions(+)
 create mode 100644 drivers/power/supply/acer_a500_battery.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index eec646c568b7..bc493173ddbc 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -774,4 +774,10 @@ config RN5T618_POWER
 	  This driver can also be built as a module. If so, the module will be
 	  called rn5t618_power.
 
+config BATTERY_ACER_A500
+	tristate "Acer Iconia Tab A500 battery driver"
+	depends on MFD_ACER_A500_EC
+	help
+	  Say Y to include support for Acer Iconia Tab A500 battery fuel gauge.
+
 endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index dd4b86318cd9..0607a3d64c0f 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -98,3 +98,4 @@ obj-$(CONFIG_CHARGER_BD70528)	+= bd70528-charger.o
 obj-$(CONFIG_CHARGER_BD99954)	+= bd99954-charger.o
 obj-$(CONFIG_CHARGER_WILCO)	+= wilco-charger.o
 obj-$(CONFIG_RN5T618_POWER)	+= rn5t618_power.o
+obj-$(CONFIG_BATTERY_ACER_A500)	+= acer_a500_battery.o
diff --git a/drivers/power/supply/acer_a500_battery.c b/drivers/power/supply/acer_a500_battery.c
new file mode 100644
index 000000000000..93135933c8af
--- /dev/null
+++ b/drivers/power/supply/acer_a500_battery.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Battery driver for Acer Iconia Tab A500.
+ *
+ * Copyright 2020 GRATE-driver project.
+ *
+ * Based on downstream driver from Acer Inc.
+ * Based on NVIDIA Gas Gauge driver for SBS Compliant Batteries.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+enum {
+	REG_CAPACITY,
+	REG_VOLTAGE,
+	REG_CURRENT,
+	REG_DESIGN_CAPACITY,
+	REG_TEMPERATURE,
+};
+
+#define EC_DATA(_reg, _psp) {			\
+	.psp = POWER_SUPPLY_PROP_ ## _psp,	\
+	.reg = _reg,				\
+}
+
+static const struct battery_register {
+	enum power_supply_property psp;
+	unsigned int reg;
+} ec_data[] = {
+	[REG_CAPACITY]		= EC_DATA(0x00, CAPACITY),
+	[REG_VOLTAGE]		= EC_DATA(0x01, VOLTAGE_NOW),
+	[REG_CURRENT]		= EC_DATA(0x03, CURRENT_NOW),
+	[REG_DESIGN_CAPACITY]	= EC_DATA(0x08, CHARGE_FULL_DESIGN),
+	[REG_TEMPERATURE]	= EC_DATA(0x0a, TEMP),
+};
+
+static const enum power_supply_property a500_battery_properties[] = {
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+struct a500_battery {
+	struct delayed_work poll_work;
+	struct power_supply *psy;
+	struct regmap *rmap;
+	unsigned int capacity;
+};
+
+static bool a500_battery_update_capacity(struct a500_battery *bat)
+{
+	unsigned int capacity;
+	int err;
+
+	err = regmap_read(bat->rmap, ec_data[REG_CAPACITY].reg, &capacity);
+	if (err)
+		return false;
+
+	/* capacity can be >100% even if max value is 100% */
+	capacity = min(capacity, 100u);
+
+	if (bat->capacity != capacity) {
+		bat->capacity = capacity;
+		return true;
+	}
+
+	return false;
+}
+
+static int a500_battery_get_status(struct a500_battery *bat)
+{
+	if (bat->capacity < 100) {
+		if (power_supply_am_i_supplied(bat->psy))
+			return POWER_SUPPLY_STATUS_CHARGING;
+		else
+			return POWER_SUPPLY_STATUS_DISCHARGING;
+	}
+
+	return POWER_SUPPLY_STATUS_FULL;
+}
+
+static void a500_battery_unit_adjustment(struct device *dev,
+					 enum power_supply_property psp,
+					 union power_supply_propval *val)
+{
+	const unsigned int base_unit_conversion = 1000;
+	const unsigned int temp_kelvin_to_celsius = 2731;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval *= base_unit_conversion;
+		break;
+
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval -= temp_kelvin_to_celsius;
+		break;
+
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = !!val->intval;
+		break;
+
+	default:
+		dev_dbg(dev,
+			"%s: no need for unit conversion %d\n", __func__, psp);
+	}
+}
+
+static int a500_battery_get_ec_data_index(struct device *dev,
+					  enum power_supply_property psp)
+{
+	unsigned int i;
+
+	/*
+	 * DESIGN_CAPACITY register always returns a non-zero value if
+	 * battery is connected and zero if disconnected, hence we'll use
+	 * it for judging the battery presence.
+	 */
+	if (psp == POWER_SUPPLY_PROP_PRESENT)
+		psp = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+
+	for (i = 0; i < ARRAY_SIZE(ec_data); i++)
+		if (psp == ec_data[i].psp)
+			return i;
+
+	dev_dbg(dev, "%s: invalid property %u\n", __func__, psp);
+
+	return -EINVAL;
+}
+
+static int a500_battery_get_property(struct power_supply *psy,
+				     enum power_supply_property psp,
+				     union power_supply_propval *val)
+{
+	struct a500_battery *bat = power_supply_get_drvdata(psy);
+	struct device *dev = psy->dev.parent;
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = a500_battery_get_status(bat);
+		break;
+
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+
+	case POWER_SUPPLY_PROP_CAPACITY:
+		a500_battery_update_capacity(bat);
+		val->intval = bat->capacity;
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = a500_battery_get_ec_data_index(dev, psp);
+		if (ret < 0)
+			break;
+
+		ret = regmap_read(bat->rmap, ec_data[ret].reg, &val->intval);
+		break;
+
+	default:
+		dev_err(dev, "%s: invalid property %u\n", __func__, psp);
+		return -EINVAL;
+	}
+
+	if (!ret) {
+		/* convert units to match requirements of power supply class */
+		a500_battery_unit_adjustment(dev, psp, val);
+	}
+
+	dev_dbg(dev, "%s: property = %d, value = %x\n",
+		__func__, psp, val->intval);
+
+	/* return NODATA for properties if battery not presents */
+	if (ret)
+		return -ENODATA;
+
+	return 0;
+}
+
+static void a500_battery_poll_work(struct work_struct *work)
+{
+	struct a500_battery *bat;
+	bool capacity_changed;
+
+	bat = container_of(work, struct a500_battery, poll_work.work);
+	capacity_changed = a500_battery_update_capacity(bat);
+
+	if (capacity_changed)
+		power_supply_changed(bat->psy);
+
+	/* continuously send uevent notification */
+	schedule_delayed_work(&bat->poll_work, 30 * HZ);
+}
+
+static const struct power_supply_desc a500_battery_desc = {
+	.name = "ec-battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = a500_battery_properties,
+	.get_property = a500_battery_get_property,
+	.num_properties = ARRAY_SIZE(a500_battery_properties),
+	.external_power_changed = power_supply_changed,
+};
+
+static int a500_battery_probe(struct platform_device *pdev)
+{
+	struct power_supply_config psy_cfg = {};
+	struct a500_battery *bat;
+
+	bat = devm_kzalloc(&pdev->dev, sizeof(*bat), GFP_KERNEL);
+	if (!bat)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, bat);
+
+	psy_cfg.of_node = pdev->dev.parent->of_node;
+	psy_cfg.drv_data = bat;
+
+	bat->rmap = dev_get_regmap(pdev->dev.parent, "KB930");
+	if (!bat->rmap)
+		return -EINVAL;
+
+	bat->psy = devm_power_supply_register_no_ws(&pdev->dev,
+						    &a500_battery_desc,
+						    &psy_cfg);
+	if (IS_ERR(bat->psy))
+		return dev_err_probe(&pdev->dev, PTR_ERR(bat->psy),
+				     "failed to register battery\n");
+
+	INIT_DELAYED_WORK(&bat->poll_work, a500_battery_poll_work);
+	schedule_delayed_work(&bat->poll_work, HZ);
+
+	return 0;
+}
+
+static int a500_battery_remove(struct platform_device *pdev)
+{
+	struct a500_battery *bat = dev_get_drvdata(&pdev->dev);
+
+	cancel_delayed_work_sync(&bat->poll_work);
+
+	return 0;
+}
+
+static int __maybe_unused a500_battery_suspend(struct device *dev)
+{
+	struct a500_battery *bat = dev_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&bat->poll_work);
+
+	return 0;
+}
+
+static int __maybe_unused a500_battery_resume(struct device *dev)
+{
+	struct a500_battery *bat = dev_get_drvdata(dev);
+
+	schedule_delayed_work(&bat->poll_work, HZ);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(a500_battery_pm_ops,
+			 a500_battery_suspend, a500_battery_resume);
+
+static struct platform_driver a500_battery_driver = {
+	.driver = {
+		.name = "acer-a500-iconia-battery",
+		.pm = &a500_battery_pm_ops,
+	},
+	.probe = a500_battery_probe,
+	.remove = a500_battery_remove,
+};
+module_platform_driver(a500_battery_driver);
+
+MODULE_DESCRIPTION("Battery gauge driver for Acer Iconia Tab A500");
+MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
+MODULE_ALIAS("platform:acer-a500-iconia-battery");
+MODULE_LICENSE("GPL");
-- 
2.27.0


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

* [PATCH v5 4/4] ARM: tegra: acer-a500: Add Embedded Controller
  2020-11-04 20:33 [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500 Dmitry Osipenko
                   ` (2 preceding siblings ...)
  2020-11-04 20:34 ` [PATCH v5 3/4] power: supply: Add battery gauge driver for " Dmitry Osipenko
@ 2020-11-04 20:34 ` Dmitry Osipenko
  3 siblings, 0 replies; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-04 20:34 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Thierry Reding, Jonathan Hunter,
	Dan Murphy, Sebastian Reichel
  Cc: devicetree, linux-tegra, linux-pm, linux-kernel

This patch adds device-tree node for the Embedded Controller which is
found on the Picasso board. The Embedded Controller itself is ENE KB930,
it provides functions like battery-gauge/LED/GPIO/etc and it uses firmware
that is specifically customized for the Acer A500 device.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/boot/dts/tegra20-acer-a500-picasso.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts b/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
index 5ab6872cd84c..3b9ac3324fd5 100644
--- a/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
+++ b/arch/arm/boot/dts/tegra20-acer-a500-picasso.dts
@@ -533,6 +533,16 @@ panel_ddc: i2c@1 {
 			reg = <1>;
 			#address-cells = <1>;
 			#size-cells = <0>;
+
+			embedded-controller@58 {
+				compatible = "acer,a500-iconia-ec", "ene,kb930";
+				reg = <0x58>;
+
+				system-power-controller;
+
+				monitored-battery = <&bat1010>;
+				power-supplies = <&mains>;
+			};
 		};
 	};
 
@@ -820,6 +830,13 @@ backlight: backlight {
 		default-brightness-level = <20>;
 	};
 
+	bat1010: battery-2s1p {
+		compatible = "simple-battery";
+		charge-full-design-microamp-hours = <3260000>;
+		energy-full-design-microwatt-hours = <24000000>;
+		operating-range-celsius = <0 40>;
+	};
+
 	/* PMIC has a built-in 32KHz oscillator which is used by PMC */
 	clk32k_in: clock@0 {
 		compatible = "fixed-clock";
-- 
2.27.0


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

* Re: [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding
  2020-11-04 20:34 ` [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Dmitry Osipenko
@ 2020-11-06 16:11   ` Rob Herring
  0 siblings, 0 replies; 11+ messages in thread
From: Rob Herring @ 2020-11-06 16:11 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Sebastian Reichel, Thierry Reding, Lee Jones, linux-kernel,
	Jonathan Hunter, Dan Murphy, devicetree, linux-tegra,
	Rob Herring, linux-pm

On Wed, 04 Nov 2020 23:34:00 +0300, Dmitry Osipenko wrote:
> Add binding document for the ENE KB930 Embedded Controller.
> 
> Reviewed-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  .../devicetree/bindings/mfd/ene-kb930.yaml    | 66 +++++++++++++++++++
>  1 file changed, 66 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/ene-kb930.yaml
> 


My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:
./Documentation/devicetree/bindings/mfd/ene-kb930.yaml:20:9: [warning] wrong indentation: expected 10 but found 8 (indentation)
./Documentation/devicetree/bindings/mfd/ene-kb930.yaml:22:9: [warning] wrong indentation: expected 10 but found 8 (indentation)

dtschema/dtc warnings/errors:


See https://patchwork.ozlabs.org/patch/1394517

The base for the patch is generally the last rc1. Any dependencies
should be noted.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


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

* Re: [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-04 20:34 ` [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500 Dmitry Osipenko
@ 2020-11-13  9:37   ` Lee Jones
  2020-11-15 23:12     ` Dmitry Osipenko
  0 siblings, 1 reply; 11+ messages in thread
From: Lee Jones @ 2020-11-13  9:37 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Dan Murphy,
	Sebastian Reichel, devicetree, linux-tegra, linux-pm,
	linux-kernel

On Wed, 04 Nov 2020, Dmitry Osipenko wrote:

> Acer Iconia Tab A500 is an Android tablet device, it has ENE KB930
> Embedded Controller which provides battery-gauge, LED, GPIO and some
> other functions. The EC uses firmware that is specifically customized
> for Acer A500. This patch adds MFD driver for the Embedded Controller
> which allows to power-off / reboot the A500 device, it also provides
> a common register read/write API that will be used by the sub-devices.
> 
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/mfd/Kconfig        |  12 +++
>  drivers/mfd/Makefile       |   1 +
>  drivers/mfd/acer-ec-a500.c | 203 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 216 insertions(+)
>  create mode 100644 drivers/mfd/acer-ec-a500.c
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 8b99a13669bf..527ba5054d80 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -2097,6 +2097,18 @@ config MFD_KHADAS_MCU
>  	  additional drivers must be enabled in order to use the functionality
>  	  of the device.
>  
> +config MFD_ACER_A500_EC
> +	tristate "Embedded Controller driver for Acer Iconia Tab A500"

Drop "driver".  This is meant to be describing the device.

> +	depends on I2C

depends on OF ?

> +	depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	help
> +	  Support for Acer Iconia Tab A500 Embedded Controller.
> +
> +	  The controller itself is ENE KB930, it is running firmware
> +	  customized for the specific needs of the Acer A500 hardware.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100
>  
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 1780019d2474..7bfc57c8b363 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828)	+= rohm-bd71828.o
>  obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
>  obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
>  obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
> +obj-$(CONFIG_MFD_ACER_A500_EC)	+= acer-ec-a500.o
>  
>  obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
>  obj-$(CONFIG_MFD_SIMPLE_MFD_I2C)	+= simple-mfd-i2c.o
> diff --git a/drivers/mfd/acer-ec-a500.c b/drivers/mfd/acer-ec-a500.c
> new file mode 100644
> index 000000000000..2785a6d9bcc4
> --- /dev/null
> +++ b/drivers/mfd/acer-ec-a500.c
> @@ -0,0 +1,203 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * MFD driver for Acer Iconia Tab A500 Embedded Controller.

An "MFD" isn't a thing.  Please describe the device.

> + * Copyright 2020 GRATE-driver project.
> + *
> + * Based on downstream driver from Acer Inc.

Most drivers are.  Not sure this is required.

> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reboot.h>

Alphabetical please. ;)

> +#define A500_EC_I2C_ERR_TIMEOUT		500
> +#define A500_EC_POWER_CMD_TIMEOUT	1000
> +
> +enum {
> +	REG_CURRENT_NOW = 0x03,
> +	REG_SHUTDOWN = 0x52,
> +	REG_WARM_REBOOT = 0x54,
> +	REG_COLD_REBOOT = 0x55,
> +};
> +
> +static struct i2c_client *a500_ec_client_pm_off;
> +
> +static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
> +			void *val_buf, size_t val_sizel)
> +{
> +	struct i2c_client *client = context;
> +	unsigned int reg, retries = 5;
> +	u16 *ret_val = val_buf;
> +	s32 ret = 0;
> +
> +	if (reg_size != 1 || val_sizel != 2)

No magic numbers please.

What does this *mean*?

> +		return -EOPNOTSUPP;

Why EOPNOTSUPP?

> +	reg = *(u8 *)reg_buf;
> +
> +	while (retries-- > 0) {
> +		ret = i2c_smbus_read_word_data(client, reg);
> +		if (ret >= 0)
> +			break;
> +
> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
> +		return ret;
> +	}
> +
> +	*ret_val = ret;
> +
> +	if (reg == REG_CURRENT_NOW)
> +		fsleep(10000);

Ooo, new toy!

> +	return 0;
> +}

I'm surprised there isn't a generic function which does this kind of
read.  Seems like pretty common/boilerplate stuff.

> +static int a500_ec_write(void *context, const void *data, size_t count)
> +{
> +	struct i2c_client *client = context;
> +	unsigned int reg, val, retries = 5;
> +	s32 ret = 0;
> +
> +	if (count != 3)

Define or comment needed.

> +		return -EOPNOTSUPP;
> +
> +	reg = *(u8  *)(data + 0);
> +	val = *(u16 *)(data + 1);
> +
> +	while (retries-- > 0) {
> +		ret = i2c_smbus_write_word_data(client, reg, val);
> +		if (ret >= 0)
> +			break;
> +
> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "write 0x%x failed: %d\n", reg, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}

Again, seems pretty boilerplate.  Are you sure there isn't a helper?

> +static const struct regmap_config a500_ec_regmap_config = {
> +	.name = "KB930",
> +	.reg_bits = 8,
> +	.val_bits = 16,
> +	.max_register = 0xff,
> +};
> +
> +static const struct regmap_bus a500_ec_regmap_bus = {
> +	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
> +	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
> +	.write = a500_ec_write,
> +	.read = a500_ec_read,
> +	.max_raw_read = 2,
> +};
> +
> +static void a500_ec_poweroff(void)
> +{
> +	i2c_smbus_write_word_data(a500_ec_client_pm_off, REG_SHUTDOWN, 0);
> +
> +	mdelay(A500_EC_POWER_CMD_TIMEOUT);
> +}
> +
> +static int a500_ec_restart_notify(struct notifier_block *this,
> +				  unsigned long reboot_mode, void *data)
> +{
> +	if (reboot_mode == REBOOT_WARM)
> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
> +					  REG_WARM_REBOOT, 0);
> +	else
> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
> +					  REG_COLD_REBOOT, 1);

What's with the magic '0' and '1's at the end?

> +	mdelay(A500_EC_POWER_CMD_TIMEOUT);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block a500_ec_restart_handler = {
> +	.notifier_call = a500_ec_restart_notify,
> +	.priority = 200,
> +};
> +
> +static const struct mfd_cell a500_ec_cells[] = {
> +	{ .name = "acer-a500-iconia-battery", },
> +	{ .name = "acer-a500-iconia-leds", },
> +};
> +
> +static int a500_ec_probe(struct i2c_client *client)
> +{
> +	struct regmap *rmap;

'rmap' barely makes the top 10:

$ git grep -ho "struct regmap \*[a-z]*" | sort | uniq -c | sort -rn | head
   1814 struct regmap *regmap
    581 struct regmap *map
     97 struct regmap *
     50 struct regmap *syscon
     50 struct regmap *r
     35 struct regmap *reg
     34 struct regmap *cfgchip
     32 struct regmap *grf
     30 struct regmap *rmap
     27 struct regmap *base

Please use regmap here.

> +	int err;
> +
> +	rmap = devm_regmap_init(&client->dev, &a500_ec_regmap_bus,
> +				client, &a500_ec_regmap_config);
> +	if (IS_ERR(rmap))
> +		return PTR_ERR(rmap);
> +
> +	/* register battery and LED sub-devices */

This comment is superfluous and is just the type of documentation that
becomes out-of-date quickly.

> +	err = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
> +				   a500_ec_cells, ARRAY_SIZE(a500_ec_cells),
> +				   NULL, 0, NULL);
> +	if (err) {
> +		dev_err(&client->dev, "failed to add sub-devices: %d\n", err);
> +		return err;
> +	}
> +
> +	/* set up power management functions */

We know what "power" and "pm" mean.  You can safely remove this
comment.

> +	if (of_device_is_system_power_controller(client->dev.of_node)) {
> +		a500_ec_client_pm_off = client;
> +
> +		err = register_restart_handler(&a500_ec_restart_handler);
> +		if (err)
> +			return err;
> +
> +		if (!pm_power_off)
> +			pm_power_off = a500_ec_poweroff;
> +	}
> +
> +	return 0;
> +}
> +
> +static int a500_ec_remove(struct i2c_client *client)
> +{
> +	if (of_device_is_system_power_controller(client->dev.of_node)) {
> +		if (pm_power_off == a500_ec_poweroff)
> +			pm_power_off = NULL;
> +
> +		unregister_restart_handler(&a500_ec_restart_handler);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id a500_ec_match[] = {
> +	{ .compatible = "acer,a500-iconia-ec" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, a500_ec_match);
> +
> +static struct i2c_driver a500_ec_driver = {
> +	.driver = {
> +		.name = "acer-a500-embedded-controller",
> +		.of_match_table = a500_ec_match,
> +	},
> +	.probe_new = a500_ec_probe,
> +	.remove = a500_ec_remove,
> +};
> +module_i2c_driver(a500_ec_driver);
> +
> +MODULE_DESCRIPTION("Acer Iconia Tab A500 Embedded Controller driver");
> +MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
> +MODULE_LICENSE("GPL");

-- 
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-13  9:37   ` Lee Jones
@ 2020-11-15 23:12     ` Dmitry Osipenko
  2020-11-16  8:48       ` Lee Jones
  0 siblings, 1 reply; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-15 23:12 UTC (permalink / raw)
  To: Lee Jones
  Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Dan Murphy,
	Sebastian Reichel, devicetree, linux-tegra, linux-pm,
	linux-kernel

13.11.2020 12:37, Lee Jones пишет:
...
>> +config MFD_ACER_A500_EC
>> +	tristate "Embedded Controller driver for Acer Iconia Tab A500"
> 
> Drop "driver".  This is meant to be describing the device.
> 
>> +	depends on I2C
> 
> depends on OF ?
...
>> +	depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
>> +	select MFD_CORE
>> +	select REGMAP
>> +	help

ARCH_TEGRA_2x_SOC selects OF.

For COMPILE_TEST it doesn't matter since OF framework provides stubs for
!OF.

...
>> +static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
>> +			void *val_buf, size_t val_sizel)
>> +{
>> +	struct i2c_client *client = context;
>> +	unsigned int reg, retries = 5;
>> +	u16 *ret_val = val_buf;
>> +	s32 ret = 0;
>> +
>> +	if (reg_size != 1 || val_sizel != 2)
> 
> No magic numbers please.
> 
> What does this *mean*?

These are the size of address register and size of a read value, both in
bytes.

>> +		return -EOPNOTSUPP;
> 
> Why EOPNOTSUPP?

Other sizes aren't supported by embedded controller.

Although, perhaps this check isn't really needed at all since the sizes
are already expressed in the a500_ec_regmap_config.

I'll need to take a closer look at why this size-checking was added
because don't quite remember already. If it's not needed, then I'll
remove it in the next revision, otherwise will add a clarifying comment.

>> +	reg = *(u8 *)reg_buf;
>> +
>> +	while (retries-- > 0) {
>> +		ret = i2c_smbus_read_word_data(client, reg);
>> +		if (ret >= 0)
>> +			break;
>> +
>> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
>> +	}
>> +
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
>> +		return ret;
>> +	}
>> +
>> +	*ret_val = ret;
>> +
>> +	if (reg == REG_CURRENT_NOW)
>> +		fsleep(10000);
> 
> Ooo, new toy!
> 
>> +	return 0;
>> +}
> 
> I'm surprised there isn't a generic function which does this kind of
> read.  Seems like pretty common/boilerplate stuff.

I'm not quite sure that this is a really very common pattern which
deserves a generic helper.

...
>> +static int a500_ec_restart_notify(struct notifier_block *this,
>> +				  unsigned long reboot_mode, void *data)
>> +{
>> +	if (reboot_mode == REBOOT_WARM)
>> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
>> +					  REG_WARM_REBOOT, 0);
>> +	else
>> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
>> +					  REG_COLD_REBOOT, 1);
> 
> What's with the magic '0' and '1's at the end?

These are the values which controller's firmware wants to see, otherwise
it will reject command as invalid. I'll add a clarifying comment to the
code.

Thank you for the review. I'll address all the comments in the v7.

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

* Re: [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-15 23:12     ` Dmitry Osipenko
@ 2020-11-16  8:48       ` Lee Jones
  2020-11-16  9:53         ` Dmitry Osipenko
  0 siblings, 1 reply; 11+ messages in thread
From: Lee Jones @ 2020-11-16  8:48 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Dan Murphy,
	Sebastian Reichel, devicetree, linux-tegra, linux-pm,
	linux-kernel

On Mon, 16 Nov 2020, Dmitry Osipenko wrote:

> 13.11.2020 12:37, Lee Jones пишет:
> ...
> >> +config MFD_ACER_A500_EC
> >> +	tristate "Embedded Controller driver for Acer Iconia Tab A500"
> > 
> > Drop "driver".  This is meant to be describing the device.
> > 
> >> +	depends on I2C
> > 
> > depends on OF ?
> ...
> >> +	depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
> >> +	select MFD_CORE
> >> +	select REGMAP
> >> +	help
> 
> ARCH_TEGRA_2x_SOC selects OF.
> 
> For COMPILE_TEST it doesn't matter since OF framework provides stubs for
> !OF.

I always thought it was best to be explicit.

> ...
> >> +static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
> >> +			void *val_buf, size_t val_sizel)
> >> +{
> >> +	struct i2c_client *client = context;
> >> +	unsigned int reg, retries = 5;
> >> +	u16 *ret_val = val_buf;
> >> +	s32 ret = 0;
> >> +
> >> +	if (reg_size != 1 || val_sizel != 2)
> > 
> > No magic numbers please.
> > 
> > What does this *mean*?
> 
> These are the size of address register and size of a read value, both in
> bytes.

Great.  But don't tell me that.  You need to tell the next person who
reads this code, and the next, and the next.  Defines work best for
this.  Comments are also okay.

> >> +		return -EOPNOTSUPP;
> > 
> > Why EOPNOTSUPP?
> 
> Other sizes aren't supported by embedded controller.
> 
> Although, perhaps this check isn't really needed at all since the sizes
> are already expressed in the a500_ec_regmap_config.
> 
> I'll need to take a closer look at why this size-checking was added
> because don't quite remember already. If it's not needed, then I'll
> remove it in the next revision, otherwise will add a clarifying comment.

"Operation not supported on transport endpoint" doesn't seem like a
good fit here.  If the check says, please consider changing it to
something like -EINVAL.

> >> +	reg = *(u8 *)reg_buf;
> >> +
> >> +	while (retries-- > 0) {
> >> +		ret = i2c_smbus_read_word_data(client, reg);
> >> +		if (ret >= 0)
> >> +			break;
> >> +
> >> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
> >> +	}
> >> +
> >> +	if (ret < 0) {
> >> +		dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	*ret_val = ret;
> >> +
> >> +	if (reg == REG_CURRENT_NOW)
> >> +		fsleep(10000);
> > 
> > Ooo, new toy!
> > 
> >> +	return 0;
> >> +}
> > 
> > I'm surprised there isn't a generic function which does this kind of
> > read.  Seems like pretty common/boilerplate stuff.
> 
> I'm not quite sure that this is a really very common pattern which
> deserves a generic helper.

What?  Read from I2C a few times, then sleep sounds like a pretty
common pattern to me.

> ...
> >> +static int a500_ec_restart_notify(struct notifier_block *this,
> >> +				  unsigned long reboot_mode, void *data)
> >> +{
> >> +	if (reboot_mode == REBOOT_WARM)
> >> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
> >> +					  REG_WARM_REBOOT, 0);
> >> +	else
> >> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
> >> +					  REG_COLD_REBOOT, 1);
> > 
> > What's with the magic '0' and '1's at the end?
> 
> These are the values which controller's firmware wants to see, otherwise
> it will reject command as invalid. I'll add a clarifying comment to the
> code.

Thanks.  Hopefully with a bit more information as to why the firmware
expects to see them.  Hopefully they're not random.

-- 
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-16  8:48       ` Lee Jones
@ 2020-11-16  9:53         ` Dmitry Osipenko
  2020-11-16 10:29           ` Lee Jones
  0 siblings, 1 reply; 11+ messages in thread
From: Dmitry Osipenko @ 2020-11-16  9:53 UTC (permalink / raw)
  To: Lee Jones
  Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Dan Murphy,
	Sebastian Reichel, devicetree, linux-tegra, linux-pm,
	linux-kernel

16.11.2020 11:48, Lee Jones пишет:
>>>> +config MFD_ACER_A500_EC
>>>> +	tristate "Embedded Controller driver for Acer Iconia Tab A500"
>>>
>>> Drop "driver".  This is meant to be describing the device.
>>>
>>>> +	depends on I2C
>>>
>>> depends on OF ?
>> ...
>>>> +	depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
>>>> +	select MFD_CORE
>>>> +	select REGMAP
>>>> +	help
>>
>> ARCH_TEGRA_2x_SOC selects OF.
>>
>> For COMPILE_TEST it doesn't matter since OF framework provides stubs for
>> !OF.
> 
> I always thought it was best to be explicit.

Alright, I see that the OF selection is all over the MFD Kconfig, hence
let's keep it consistent.

I also prefer the explicit variant more, but some other kernel
maintainers may disagree.

>> ...
>>> Why EOPNOTSUPP?
>>
>> Other sizes aren't supported by embedded controller.
>>
>> Although, perhaps this check isn't really needed at all since the sizes
>> are already expressed in the a500_ec_regmap_config.
>>
>> I'll need to take a closer look at why this size-checking was added
>> because don't quite remember already. If it's not needed, then I'll
>> remove it in the next revision, otherwise will add a clarifying comment.
> 
> "Operation not supported on transport endpoint" doesn't seem like a
> good fit here.  If the check says, please consider changing it to
> something like -EINVAL.

The regmap core code performs all the necessary checks before driver's
code is reached, perhaps I just overlooked something before. Hence it
will be removed in the next revision.

...
>>>> +	while (retries-- > 0) {
>>>> +		ret = i2c_smbus_read_word_data(client, reg);
>>>> +		if (ret >= 0)
>>>> +			break;
>>>> +
>>>> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
>>>> +	}
...
>>> I'm surprised there isn't a generic function which does this kind of
>>> read.  Seems like pretty common/boilerplate stuff.
>>
>> I'm not quite sure that this is a really very common pattern which
>> deserves a generic helper.
> 
> What?  Read from I2C a few times, then sleep sounds like a pretty
> common pattern to me.

Maybe the read_poll_timeout() helper could be used for that, but I think
the open-coded variant is much easier to perceive, don't you agree?

>> ...
>>>> +static int a500_ec_restart_notify(struct notifier_block *this,
>>>> +				  unsigned long reboot_mode, void *data)
>>>> +{
>>>> +	if (reboot_mode == REBOOT_WARM)
>>>> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
>>>> +					  REG_WARM_REBOOT, 0);
>>>> +	else
>>>> +		i2c_smbus_write_word_data(a500_ec_client_pm_off,
>>>> +					  REG_COLD_REBOOT, 1);
>>>
>>> What's with the magic '0' and '1's at the end?
>>
>> These are the values which controller's firmware wants to see, otherwise
>> it will reject command as invalid. I'll add a clarifying comment to the
>> code.
> 
> Thanks.  Hopefully with a bit more information as to why the firmware
> expects to see them.  Hopefully they're not random.
> 

These are the firmware-defined specific command opcodes, I'll add
defines for them to make it more clear, thanks.

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

* Re: [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500
  2020-11-16  9:53         ` Dmitry Osipenko
@ 2020-11-16 10:29           ` Lee Jones
  0 siblings, 0 replies; 11+ messages in thread
From: Lee Jones @ 2020-11-16 10:29 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Rob Herring, Thierry Reding, Jonathan Hunter, Dan Murphy,
	Sebastian Reichel, devicetree, linux-tegra, linux-pm,
	linux-kernel

On Mon, 16 Nov 2020, Dmitry Osipenko wrote:
> ...
> >>>> +	while (retries-- > 0) {
> >>>> +		ret = i2c_smbus_read_word_data(client, reg);
> >>>> +		if (ret >= 0)
> >>>> +			break;
> >>>> +
> >>>> +		msleep(A500_EC_I2C_ERR_TIMEOUT);
> >>>> +	}
> ...
> >>> I'm surprised there isn't a generic function which does this kind of
> >>> read.  Seems like pretty common/boilerplate stuff.
> >>
> >> I'm not quite sure that this is a really very common pattern which
> >> deserves a generic helper.
> > 
> > What?  Read from I2C a few times, then sleep sounds like a pretty
> > common pattern to me.
> 
> Maybe the read_poll_timeout() helper could be used for that, but I think
> the open-coded variant is much easier to perceive, don't you agree?

I haven't looked into it.  My comment was more general in nature.

As a general rule, helpers are better than open coding, but there are
always exceptions.  There may not even be a suitable helper for this
use-case.  As I say, the comment was more of a passing remark.

[...]

> >> These are the values which controller's firmware wants to see, otherwise
> >> it will reject command as invalid. I'll add a clarifying comment to the
> >> code.
> > 
> > Thanks.  Hopefully with a bit more information as to why the firmware
> > expects to see them.  Hopefully they're not random.
> 
> These are the firmware-defined specific command opcodes, I'll add
> defines for them to make it more clear, thanks.

That'll do.

-- 
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog

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

end of thread, other threads:[~2020-11-16 11:44 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-04 20:33 [PATCH v5 0/4] Introduce Embedded Controller driver for Acer A500 Dmitry Osipenko
2020-11-04 20:34 ` [PATCH v5 1/4] dt-bindings: mfd: Add ENE KB930 Embedded Controller binding Dmitry Osipenko
2020-11-06 16:11   ` Rob Herring
2020-11-04 20:34 ` [PATCH v5 2/4] mfd: Add driver for Embedded Controller found on Acer Iconia Tab A500 Dmitry Osipenko
2020-11-13  9:37   ` Lee Jones
2020-11-15 23:12     ` Dmitry Osipenko
2020-11-16  8:48       ` Lee Jones
2020-11-16  9:53         ` Dmitry Osipenko
2020-11-16 10:29           ` Lee Jones
2020-11-04 20:34 ` [PATCH v5 3/4] power: supply: Add battery gauge driver for " Dmitry Osipenko
2020-11-04 20:34 ` [PATCH v5 4/4] ARM: tegra: acer-a500: Add Embedded Controller Dmitry Osipenko

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