linux-watchdog.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs
@ 2023-07-06 15:48 Henning Schild
  2023-07-06 15:48 ` [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed Henning Schild
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Henning Schild @ 2023-07-06 15:48 UTC (permalink / raw)
  To: Hans de Goede, Mark Gross, linux-kernel, platform-driver-x86
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler, Henning Schild

The real change is in p2 and p1 just prepares to multiplex an IO region.
We introduce a custom driver to read CMOS battery information on several
of the Industrial PCs.

This is based on
 "[PATCH 0/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"

Henning Schild (2):
  watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  platform/x86: add CMOS battery monitoring for simatic IPCs

 drivers/platform/x86/Kconfig                  |  48 ++++
 drivers/platform/x86/Makefile                 |   6 +-
 .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
 .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
 .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
 drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
 drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
 drivers/platform/x86/simatic-ipc.c            |  65 ++++-
 drivers/watchdog/simatic-ipc-wdt.c            |   9 +-
 .../platform_data/x86/simatic-ipc-base.h      |   1 +
 10 files changed, 553 insertions(+), 18 deletions(-)
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt.h

-- 
2.39.3


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

* [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  2023-07-06 15:48 [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
@ 2023-07-06 15:48 ` Henning Schild
  2023-07-06 16:03   ` Guenter Roeck
  2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
  2023-07-14  9:28 ` [PATCH 0/2] " Hans de Goede
  2 siblings, 1 reply; 14+ messages in thread
From: Henning Schild @ 2023-07-06 15:48 UTC (permalink / raw)
  To: Hans de Goede, Mark Gross, linux-kernel, platform-driver-x86
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler, Henning Schild

The IO region used for the watchdog also hold CMOS battery monitoring
information. Make the access muxed so that a hwmon driver can use the
region as well.

Signed-off-by: Henning Schild <henning.schild@siemens.com>
---
 drivers/watchdog/simatic-ipc-wdt.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c
index 6599695dc672..cdc1a2e15180 100644
--- a/drivers/watchdog/simatic-ipc-wdt.c
+++ b/drivers/watchdog/simatic-ipc-wdt.c
@@ -155,9 +155,8 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
 
 	switch (plat->devmode) {
 	case SIMATIC_IPC_DEVICE_227E:
-		if (!devm_request_region(dev, gp_status_reg_227e_res.start,
-					 resource_size(&gp_status_reg_227e_res),
-					 KBUILD_MODNAME)) {
+		res = &gp_status_reg_227e_res;
+		if (!request_muxed_region(res->start, resource_size(res), res->name)) {
 			dev_err(dev,
 				"Unable to register IO resource at %pR\n",
 				&gp_status_reg_227e_res);
@@ -210,6 +209,10 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
 	if (wdd_data.bootstatus)
 		dev_warn(dev, "last reboot caused by watchdog reset\n");
 
+	if (plat->devmode == SIMATIC_IPC_DEVICE_227E)
+		release_region(gp_status_reg_227e_res.start,
+			       resource_size(&gp_status_reg_227e_res));
+
 	watchdog_set_nowayout(&wdd_data, nowayout);
 	watchdog_stop_on_reboot(&wdd_data);
 	return devm_watchdog_register_device(dev, &wdd_data);
-- 
2.39.3


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

* [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-06 15:48 [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
  2023-07-06 15:48 ` [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed Henning Schild
@ 2023-07-06 15:48 ` Henning Schild
  2023-07-10 13:34   ` Hans de Goede
                     ` (2 more replies)
  2023-07-14  9:28 ` [PATCH 0/2] " Hans de Goede
  2 siblings, 3 replies; 14+ messages in thread
From: Henning Schild @ 2023-07-06 15:48 UTC (permalink / raw)
  To: Hans de Goede, Mark Gross, linux-kernel, platform-driver-x86
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler, Henning Schild

Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
battery with two bits that indicate low or empty state. This can be GPIO
or PortIO based.
Here we model that as a hwmon voltage. The core driver does the PortIO
and provides boilerplate for the GPIO versions. Which are split out to
model runtime dependencies while allowing fine-grained kernel
configuration.

Signed-off-by: Henning Schild <henning.schild@siemens.com>
---
 drivers/platform/x86/Kconfig                  |  48 ++++
 drivers/platform/x86/Makefile                 |   6 +-
 .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
 .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
 .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
 drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
 drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
 drivers/platform/x86/simatic-ipc.c            |  65 ++++-
 .../platform_data/x86/simatic-ipc-base.h      |   1 +
 9 files changed, 547 insertions(+), 15 deletions(-)
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
 create mode 100644 drivers/platform/x86/simatic-ipc-batt.h

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 49c2c4cd8d00..ad15063e1178 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
 	  To compile this driver as a module, choose M here: the module
 	  will be called simatic-ipc.
 
+config SIEMENS_SIMATIC_IPC_BATT
+	tristate "CMOS battery driver for Siemens Simatic IPCs"
+	depends on HWMON
+	depends on SIEMENS_SIMATIC_IPC
+	default SIEMENS_SIMATIC_IPC
+	help
+	  This option enables support for monitoring the voltage of the CMOS
+	  batteries of several Industrial PCs from Siemens.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called simatic-ipc-batt.
+
+config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
+	tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO"
+	depends on PINCTRL_BROXTON
+	depends on SIEMENS_SIMATIC_IPC_BATT
+	default SIEMENS_SIMATIC_IPC_BATT
+	help
+	  This option enables CMOS battery monitoring for Simatic Industrial PCs
+	  from Siemens based on Apollo Lake GPIO.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called simatic-ipc-batt-apollolake.
+
+config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
+	tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO"
+	depends on PINCTRL_ELKHARTLAKE
+	depends on SIEMENS_SIMATIC_IPC_BATT
+	default SIEMENS_SIMATIC_IPC_BATT
+	help
+	  This option enables CMOS battery monitoring for Simatic Industrial PCs
+	  from Siemens based on Elkhart Lake GPIO.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called simatic-ipc-batt-elkhartlake.
+
+config SIEMENS_SIMATIC_IPC_BATT_F7188X
+	tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO"
+	depends on GPIO_F7188X
+	depends on SIEMENS_SIMATIC_IPC_BATT
+	default SIEMENS_SIMATIC_IPC_BATT
+	help
+	  This option enables CMOS battery monitoring for Simatic Industrial PCs
+	  from Siemens based on Nuvoton GPIO.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called simatic-ipc-batt-elkhartlake.
+
 config WINMATE_FM07_KEYS
 	tristate "Winmate FM07/FM07P front-panel keys driver"
 	depends on INPUT
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 52dfdf574ac2..522da0d1584d 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+= intel_scu_ipcutil.o
 obj-$(CONFIG_X86_INTEL_LPSS)		+= pmc_atom.o
 
 # Siemens Simatic Industrial PCs
-obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
+obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+= simatic-ipc.o
+obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)			+= simatic-ipc-batt.o
+obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+= simatic-ipc-batt-apollolake.o
+obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+= simatic-ipc-batt-elkhartlake.o
+obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+= simatic-ipc-batt-f7188x.o
 
 # Winmate
 obj-$(CONFIG_WINMATE_FM07_KEYS)		+= winmate-fm07-keys.o
diff --git a/drivers/platform/x86/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
new file mode 100644
index 000000000000..0503cb89d8f8
--- /dev/null
+++ b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for CMOS battery monitoring
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Authors:
+ *  Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "simatic-ipc-batt.h"
+
+struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
+	.table = {
+		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH),
+		{} /* Terminating entry */
+	},
+};
+
+static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e);
+}
+
+static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e);
+}
+
+static struct platform_driver simatic_ipc_batt_driver = {
+	.probe = simatic_ipc_batt_apollolake_probe,
+	.remove = simatic_ipc_batt_apollolake_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+
+module_platform_driver(simatic_ipc_batt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
new file mode 100644
index 000000000000..ecf5ceb167f9
--- /dev/null
+++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for CMOS battery monitoring
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Authors:
+ *  Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "simatic-ipc-batt.h"
+
+struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
+	.table = {
+		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
+		{} /* Terminating entry */
+	},
+};
+
+static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
+}
+
+static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
+}
+
+static struct platform_driver simatic_ipc_batt_driver = {
+	.probe = simatic_ipc_batt_elkhartlake_probe,
+	.remove = simatic_ipc_batt_elkhartlake_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+
+module_platform_driver(simatic_ipc_batt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/platform/x86/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
new file mode 100644
index 000000000000..a0189b9289f6
--- /dev/null
+++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for CMOS battery monitoring
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Authors:
+ *  Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/simatic-ipc-base.h>
+
+#include "simatic-ipc-batt.h"
+
+struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
+	.table = {
+		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2, GPIO_ACTIVE_HIGH),
+		{} /* Terminating entry */
+	},
+};
+
+struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
+	.table = {
+		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH),
+		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH),
+		{} /* Terminating entry */
+	},
+};
+
+static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev)
+{
+	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
+
+	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
+		return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_227g);
+
+	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
+}
+
+static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev)
+{
+	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
+
+	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
+		return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_227g);
+
+	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
+}
+
+static struct platform_driver simatic_ipc_batt_driver = {
+	.probe = simatic_ipc_batt_f7188x_probe,
+	.remove = simatic_ipc_batt_f7188x_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+
+module_platform_driver(simatic_ipc_batt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/platform/x86/simatic-ipc-batt.c b/drivers/platform/x86/simatic-ipc-batt.c
new file mode 100644
index 000000000000..a83272475b9d
--- /dev/null
+++ b/drivers/platform/x86/simatic-ipc-batt.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for CMOS battery monitoring
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Authors:
+ *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
+ *  Henning Schild <henning.schild@siemens.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/simatic-ipc-base.h>
+#include <linux/sizes.h>
+
+#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h delay */
+
+#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
+#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
+#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
+
+static struct simatic_ipc_batt {
+	u8 devmode;
+	long current_state;
+	struct gpio_desc *gpios[3];
+	unsigned long last_updated_jiffies;
+} priv;
+
+static long simatic_ipc_batt_read_gpio(void)
+{
+	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
+
+	if (priv.gpios[2]) {
+		gpiod_set_value(priv.gpios[2], 1);
+		msleep(150);
+	}
+
+	if (gpiod_get_value_cansleep(priv.gpios[0]))
+		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
+	else if (gpiod_get_value_cansleep(priv.gpios[1]))
+		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
+
+	if (priv.gpios[2])
+		gpiod_set_value(priv.gpios[2], 0);
+
+	return r;
+}
+
+#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
+static struct resource simatic_ipc_batt_io_res =
+	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME);
+
+static long simatic_ipc_batt_read_io(struct device *dev)
+{
+	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
+	struct resource *res = &simatic_ipc_batt_io_res;
+	u8 val;
+
+	if (!request_muxed_region(res->start, resource_size(res), res->name)) {
+		dev_err(dev, "Unable to register IO resource at %pR\n", res);
+		return -EBUSY;
+	}
+
+	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
+	release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res));
+
+	if (val & (1 << 7))
+		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
+	else if (val & (1 << 6))
+		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
+
+	return r;
+}
+
+static long simatic_ipc_batt_read_value(struct device *dev)
+{
+	unsigned long next_update;
+
+	next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS);
+	if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) {
+		switch (priv.devmode) {
+		case SIMATIC_IPC_DEVICE_127E:
+		case SIMATIC_IPC_DEVICE_227G:
+		case SIMATIC_IPC_DEVICE_BX_39A:
+			priv.current_state = simatic_ipc_batt_read_gpio();
+			break;
+		case SIMATIC_IPC_DEVICE_227E:
+			priv.current_state = simatic_ipc_batt_read_io(dev);
+			break;
+		}
+		priv.last_updated_jiffies = jiffies;
+		if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL)
+			dev_warn(dev, "CMOS battery needs to be replaced.");
+	}
+
+	return priv.current_state;
+}
+
+static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type,
+				 u32 attr, int channel, long *val)
+{
+	switch (attr) {
+	case hwmon_in_input:
+		*val = simatic_ipc_batt_read_value(dev);
+		break;
+	case hwmon_in_lcrit:
+		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type,
+					   u32 attr, int channel)
+{
+	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops simatic_ipc_batt_ops = {
+	.is_visible = simatic_ipc_batt_is_visible,
+	.read = simatic_ipc_batt_read,
+};
+
+static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
+	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
+	NULL
+};
+
+static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
+	.ops = &simatic_ipc_batt_ops,
+	.info = simatic_ipc_batt_info,
+};
+
+int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table)
+{
+	gpiod_remove_lookup_table(table);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
+
+int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table)
+{
+	struct simatic_ipc_platform *plat;
+	struct device *dev = &pdev->dev;
+	struct device *hwmon_dev;
+	int err;
+
+	plat = pdev->dev.platform_data;
+	priv.devmode = plat->devmode;
+
+	switch (priv.devmode) {
+	case SIMATIC_IPC_DEVICE_127E:
+	case SIMATIC_IPC_DEVICE_227G:
+	case SIMATIC_IPC_DEVICE_BX_39A:
+	case SIMATIC_IPC_DEVICE_BX_21A:
+		table->dev_id = dev_name(dev);
+		gpiod_add_lookup_table(table);
+		break;
+	case SIMATIC_IPC_DEVICE_227E:
+		goto nogpio;
+	default:
+		return -ENODEV;
+	}
+
+	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN);
+	if (IS_ERR(priv.gpios[0])) {
+		err = PTR_ERR(priv.gpios[0]);
+		priv.gpios[0] = NULL;
+		goto out;
+	}
+	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN);
+	if (IS_ERR(priv.gpios[1])) {
+		err = PTR_ERR(priv.gpios[1]);
+		priv.gpios[1] = NULL;
+		goto out;
+	}
+
+	if (table->table[2].key) {
+		priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
+		if (IS_ERR(priv.gpios[2])) {
+			err = PTR_ERR(priv.gpios[1]);
+			priv.gpios[2] = NULL;
+			goto out;
+		}
+	} else {
+		priv.gpios[2] = NULL;
+	}
+
+nogpio:
+	hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME,
+							 &priv,
+							 &simatic_ipc_batt_chip_info,
+							 NULL);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto out;
+	}
+
+	/* warn about aging battery even if userspace never reads hwmon */
+	simatic_ipc_batt_read_value(dev);
+
+	return 0;
+out:
+	simatic_ipc_batt_remove(pdev, table);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
+
+static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_remove(pdev, NULL);
+}
+
+static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
+{
+	return simatic_ipc_batt_probe(pdev, NULL);
+}
+
+static struct platform_driver simatic_ipc_batt_driver = {
+	.probe = simatic_ipc_batt_io_probe,
+	.remove = simatic_ipc_batt_io_remove,
+	.driver = {
+		.name = KBUILD_MODNAME,
+	},
+};
+
+module_platform_driver(simatic_ipc_batt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/platform/x86/simatic-ipc-batt.h b/drivers/platform/x86/simatic-ipc-batt.h
new file mode 100644
index 000000000000..4545cd3e3026
--- /dev/null
+++ b/drivers/platform/x86/simatic-ipc-batt.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Siemens SIMATIC IPC driver for CMOS battery monitoring
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Author:
+ *  Henning Schild <henning.schild@siemens.com>
+ */
+
+#ifndef _SIMATIC_IPC_BATT_H
+#define _SIMATIC_IPC_BATT_H
+
+int simatic_ipc_batt_probe(struct platform_device *pdev,
+			   struct gpiod_lookup_table *table);
+
+int simatic_ipc_batt_remove(struct platform_device *pdev,
+			    struct gpiod_lookup_table *table);
+
+#endif /* _SIMATIC_IPC_BATT_H */
diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
index 4402cd354104..6d2c94c17414 100644
--- a/drivers/platform/x86/simatic-ipc.c
+++ b/drivers/platform/x86/simatic-ipc.c
@@ -21,6 +21,7 @@
 
 static struct platform_device *ipc_led_platform_device;
 static struct platform_device *ipc_wdt_platform_device;
+static struct platform_device *ipc_batt_platform_device;
 
 static const struct dmi_system_id simatic_ipc_whitelist[] = {
 	{
@@ -37,38 +38,71 @@ static struct {
 	u32 station_id;
 	u8 led_mode;
 	u8 wdt_mode;
+	u8 batt_mode;
 } device_modes[] = {
-	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
-	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
-	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
-	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
-	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
-	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
-	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
-	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
-	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
-	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
-	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE},
+	{SIMATIC_IPC_IPC127E,
+		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E},
+	{SIMATIC_IPC_IPC227D,
+		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
+	{SIMATIC_IPC_IPC227E,
+		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
+	{SIMATIC_IPC_IPC227G,
+		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
+	{SIMATIC_IPC_IPC277E,
+		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
+	{SIMATIC_IPC_IPC427D,
+		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
+	{SIMATIC_IPC_IPC427E,
+		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
+	{SIMATIC_IPC_IPC477E,
+		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
+	{SIMATIC_IPC_IPCBX_39A,
+		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
+	{SIMATIC_IPC_IPCPX_39A,
+		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
+	{SIMATIC_IPC_IPCBX_21A,
+		SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A},
 };
 
 static int register_platform_devices(u32 station_id)
 {
 	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
 	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
-	char *pdevname = KBUILD_MODNAME "_leds";
+	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
+	char *pdevname;
 	int i;
 
-	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
-
 	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
 		if (device_modes[i].station_id == station_id) {
 			ledmode = device_modes[i].led_mode;
 			wdtmode = device_modes[i].wdt_mode;
+			battmode = device_modes[i].batt_mode;
 			break;
 		}
 	}
 
+	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
+		pdevname = KBUILD_MODNAME "_batt";
+		if (battmode == SIMATIC_IPC_DEVICE_127E)
+			pdevname = KBUILD_MODNAME "_batt_apollolake";
+		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
+			pdevname = KBUILD_MODNAME "_batt_elkhartlake";
+		if (battmode == SIMATIC_IPC_DEVICE_227G || battmode == SIMATIC_IPC_DEVICE_BX_39A)
+			pdevname = KBUILD_MODNAME "_batt_f7188x";
+		platform_data.devmode = battmode;
+		ipc_batt_platform_device =
+			platform_device_register_data(NULL, pdevname,
+				PLATFORM_DEVID_NONE, &platform_data,
+				sizeof(struct simatic_ipc_platform));
+		if (IS_ERR(ipc_batt_platform_device))
+			return PTR_ERR(ipc_batt_platform_device);
+
+		pr_debug("device=%s created\n",
+			 ipc_batt_platform_device->name);
+	}
+
 	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
+		pdevname = KBUILD_MODNAME "_leds";
 		if (ledmode == SIMATIC_IPC_DEVICE_127E)
 			pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
 		if (ledmode == SIMATIC_IPC_DEVICE_227G)
@@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
 
 	platform_device_unregister(ipc_wdt_platform_device);
 	ipc_wdt_platform_device = NULL;
+
+	platform_device_unregister(ipc_batt_platform_device);
+	ipc_batt_platform_device = NULL;
 }
 
 module_init(simatic_ipc_init_module);
diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h
index 00bf18ecb160..1ba47dd6939e 100644
--- a/include/linux/platform_data/x86/simatic-ipc-base.h
+++ b/include/linux/platform_data/x86/simatic-ipc-base.h
@@ -21,6 +21,7 @@
 #define SIMATIC_IPC_DEVICE_227E 4
 #define SIMATIC_IPC_DEVICE_227G 5
 #define SIMATIC_IPC_DEVICE_BX_21A 6
+#define SIMATIC_IPC_DEVICE_BX_39A 7
 
 struct simatic_ipc_platform {
 	u8	devmode;
-- 
2.39.3


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

* Re: [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  2023-07-06 15:48 ` [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed Henning Schild
@ 2023-07-06 16:03   ` Guenter Roeck
  2023-07-10 13:33     ` Hans de Goede
  0 siblings, 1 reply; 14+ messages in thread
From: Guenter Roeck @ 2023-07-06 16:03 UTC (permalink / raw)
  To: Henning Schild, Hans de Goede, Mark Gross, linux-kernel,
	platform-driver-x86
  Cc: Wim Van Sebroeck, Jean Delvare, linux-watchdog, linux-hwmon,
	Tobias Schaffner, Gerd Haeussler

On 7/6/23 08:48, Henning Schild wrote:
> The IO region used for the watchdog also hold CMOS battery monitoring
> information. Make the access muxed so that a hwmon driver can use the
> region as well.
> 
> Signed-off-by: Henning Schild <henning.schild@siemens.com>

Acked-by: Guenter Roeck <linux@roeck-us.net>

> ---
>   drivers/watchdog/simatic-ipc-wdt.c | 9 ++++++---
>   1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c
> index 6599695dc672..cdc1a2e15180 100644
> --- a/drivers/watchdog/simatic-ipc-wdt.c
> +++ b/drivers/watchdog/simatic-ipc-wdt.c
> @@ -155,9 +155,8 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
>   
>   	switch (plat->devmode) {
>   	case SIMATIC_IPC_DEVICE_227E:
> -		if (!devm_request_region(dev, gp_status_reg_227e_res.start,
> -					 resource_size(&gp_status_reg_227e_res),
> -					 KBUILD_MODNAME)) {
> +		res = &gp_status_reg_227e_res;
> +		if (!request_muxed_region(res->start, resource_size(res), res->name)) {
>   			dev_err(dev,
>   				"Unable to register IO resource at %pR\n",
>   				&gp_status_reg_227e_res);
> @@ -210,6 +209,10 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
>   	if (wdd_data.bootstatus)
>   		dev_warn(dev, "last reboot caused by watchdog reset\n");
>   
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227E)
> +		release_region(gp_status_reg_227e_res.start,
> +			       resource_size(&gp_status_reg_227e_res));
> +
>   	watchdog_set_nowayout(&wdd_data, nowayout);
>   	watchdog_stop_on_reboot(&wdd_data);
>   	return devm_watchdog_register_device(dev, &wdd_data);


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

* Re: [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  2023-07-06 16:03   ` Guenter Roeck
@ 2023-07-10 13:33     ` Hans de Goede
  2023-07-10 13:45       ` Guenter Roeck
  0 siblings, 1 reply; 14+ messages in thread
From: Hans de Goede @ 2023-07-10 13:33 UTC (permalink / raw)
  To: Guenter Roeck, Henning Schild, Mark Gross, linux-kernel,
	platform-driver-x86
  Cc: Wim Van Sebroeck, Jean Delvare, linux-watchdog, linux-hwmon,
	Tobias Schaffner, Gerd Haeussler

Hi Guenter,

On 7/6/23 18:03, Guenter Roeck wrote:
> On 7/6/23 08:48, Henning Schild wrote:
>> The IO region used for the watchdog also hold CMOS battery monitoring
>> information. Make the access muxed so that a hwmon driver can use the
>> region as well.
>>
>> Signed-off-by: Henning Schild <henning.schild@siemens.com>
> 
> Acked-by: Guenter Roeck <linux@roeck-us.net>

Thank you. Is it ok if I pick up his patch and merge it together with 2/2
through the pdx86 tree ?

Regards,

Hans


> 
>> ---
>>   drivers/watchdog/simatic-ipc-wdt.c | 9 ++++++---
>>   1 file changed, 6 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c
>> index 6599695dc672..cdc1a2e15180 100644
>> --- a/drivers/watchdog/simatic-ipc-wdt.c
>> +++ b/drivers/watchdog/simatic-ipc-wdt.c
>> @@ -155,9 +155,8 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
>>         switch (plat->devmode) {
>>       case SIMATIC_IPC_DEVICE_227E:
>> -        if (!devm_request_region(dev, gp_status_reg_227e_res.start,
>> -                     resource_size(&gp_status_reg_227e_res),
>> -                     KBUILD_MODNAME)) {
>> +        res = &gp_status_reg_227e_res;
>> +        if (!request_muxed_region(res->start, resource_size(res), res->name)) {
>>               dev_err(dev,
>>                   "Unable to register IO resource at %pR\n",
>>                   &gp_status_reg_227e_res);
>> @@ -210,6 +209,10 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
>>       if (wdd_data.bootstatus)
>>           dev_warn(dev, "last reboot caused by watchdog reset\n");
>>   +    if (plat->devmode == SIMATIC_IPC_DEVICE_227E)
>> +        release_region(gp_status_reg_227e_res.start,
>> +                   resource_size(&gp_status_reg_227e_res));
>> +
>>       watchdog_set_nowayout(&wdd_data, nowayout);
>>       watchdog_stop_on_reboot(&wdd_data);
>>       return devm_watchdog_register_device(dev, &wdd_data);
> 


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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
@ 2023-07-10 13:34   ` Hans de Goede
  2023-07-12 18:02   ` Hans de Goede
  2023-07-12 18:05   ` Hans de Goede
  2 siblings, 0 replies; 14+ messages in thread
From: Hans de Goede @ 2023-07-10 13:34 UTC (permalink / raw)
  To: Henning Schild, Mark Gross, linux-kernel, platform-driver-x86
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler

Hi,

On 7/6/23 17:48, Henning Schild wrote:
> Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
> battery with two bits that indicate low or empty state. This can be GPIO
> or PortIO based.
> Here we model that as a hwmon voltage. The core driver does the PortIO
> and provides boilerplate for the GPIO versions. Which are split out to
> model runtime dependencies while allowing fine-grained kernel
> configuration.
> 
> Signed-off-by: Henning Schild <henning.schild@siemens.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans



> ---
>  drivers/platform/x86/Kconfig                  |  48 ++++
>  drivers/platform/x86/Makefile                 |   6 +-
>  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
>  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
>  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
>  drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
>  drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
>  drivers/platform/x86/simatic-ipc.c            |  65 ++++-
>  .../platform_data/x86/simatic-ipc-base.h      |   1 +
>  9 files changed, 547 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.h
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 49c2c4cd8d00..ad15063e1178 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called simatic-ipc.
>  
> +config SIEMENS_SIMATIC_IPC_BATT
> +	tristate "CMOS battery driver for Siemens Simatic IPCs"
> +	depends on HWMON
> +	depends on SIEMENS_SIMATIC_IPC
> +	default SIEMENS_SIMATIC_IPC
> +	help
> +	  This option enables support for monitoring the voltage of the CMOS
> +	  batteries of several Industrial PCs from Siemens.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO"
> +	depends on PINCTRL_BROXTON
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Apollo Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-apollolake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO"
> +	depends on PINCTRL_ELKHARTLAKE
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Elkhart Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_F7188X
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO"
> +	depends on GPIO_F7188X
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Nuvoton GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
>  config WINMATE_FM07_KEYS
>  	tristate "Winmate FM07/FM07P front-panel keys driver"
>  	depends on INPUT
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 52dfdf574ac2..522da0d1584d 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+= intel_scu_ipcutil.o
>  obj-$(CONFIG_X86_INTEL_LPSS)		+= pmc_atom.o
>  
>  # Siemens Simatic Industrial PCs
> -obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)			+= simatic-ipc-batt.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+= simatic-ipc-batt-apollolake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+= simatic-ipc-batt-elkhartlake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+= simatic-ipc-batt-f7188x.o
>  
>  # Winmate
>  obj-$(CONFIG_WINMATE_FM07_KEYS)		+= winmate-fm07-keys.o
> diff --git a/drivers/platform/x86/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> new file mode 100644
> index 000000000000..0503cb89d8f8
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_apollolake_probe,
> +	.remove = simatic_ipc_batt_apollolake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> new file mode 100644
> index 000000000000..ecf5ceb167f9
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_elkhartlake_probe,
> +	.remove = simatic_ipc_batt_elkhartlake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> new file mode 100644
> index 000000000000..a0189b9289f6
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_f7188x_probe,
> +	.remove = simatic_ipc_batt_f7188x_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.c b/drivers/platform/x86/simatic-ipc-batt.c
> new file mode 100644
> index 000000000000..a83272475b9d
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +#include <linux/sizes.h>
> +
> +#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h delay */
> +
> +#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
> +#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
> +#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
> +
> +static struct simatic_ipc_batt {
> +	u8 devmode;
> +	long current_state;
> +	struct gpio_desc *gpios[3];
> +	unsigned long last_updated_jiffies;
> +} priv;
> +
> +static long simatic_ipc_batt_read_gpio(void)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +
> +	if (priv.gpios[2]) {
> +		gpiod_set_value(priv.gpios[2], 1);
> +		msleep(150);
> +	}
> +
> +	if (gpiod_get_value_cansleep(priv.gpios[0]))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (gpiod_get_value_cansleep(priv.gpios[1]))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	if (priv.gpios[2])
> +		gpiod_set_value(priv.gpios[2], 0);
> +
> +	return r;
> +}
> +
> +#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
> +static struct resource simatic_ipc_batt_io_res =
> +	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME);
> +
> +static long simatic_ipc_batt_read_io(struct device *dev)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +	struct resource *res = &simatic_ipc_batt_io_res;
> +	u8 val;
> +
> +	if (!request_muxed_region(res->start, resource_size(res), res->name)) {
> +		dev_err(dev, "Unable to register IO resource at %pR\n", res);
> +		return -EBUSY;
> +	}
> +
> +	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
> +	release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res));
> +
> +	if (val & (1 << 7))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (val & (1 << 6))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	return r;
> +}
> +
> +static long simatic_ipc_batt_read_value(struct device *dev)
> +{
> +	unsigned long next_update;
> +
> +	next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS);
> +	if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) {
> +		switch (priv.devmode) {
> +		case SIMATIC_IPC_DEVICE_127E:
> +		case SIMATIC_IPC_DEVICE_227G:
> +		case SIMATIC_IPC_DEVICE_BX_39A:
> +			priv.current_state = simatic_ipc_batt_read_gpio();
> +			break;
> +		case SIMATIC_IPC_DEVICE_227E:
> +			priv.current_state = simatic_ipc_batt_read_io(dev);
> +			break;
> +		}
> +		priv.last_updated_jiffies = jiffies;
> +		if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL)
> +			dev_warn(dev, "CMOS battery needs to be replaced.");
> +	}
> +
> +	return priv.current_state;
> +}
> +
> +static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type,
> +				 u32 attr, int channel, long *val)
> +{
> +	switch (attr) {
> +	case hwmon_in_input:
> +		*val = simatic_ipc_batt_read_value(dev);
> +		break;
> +	case hwmon_in_lcrit:
> +		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type,
> +					   u32 attr, int channel)
> +{
> +	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops simatic_ipc_batt_ops = {
> +	.is_visible = simatic_ipc_batt_is_visible,
> +	.read = simatic_ipc_batt_read,
> +};
> +
> +static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
> +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
> +	NULL
> +};
> +
> +static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
> +	.ops = &simatic_ipc_batt_ops,
> +	.info = simatic_ipc_batt_info,
> +};
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	gpiod_remove_lookup_table(table);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	struct simatic_ipc_platform *plat;
> +	struct device *dev = &pdev->dev;
> +	struct device *hwmon_dev;
> +	int err;
> +
> +	plat = pdev->dev.platform_data;
> +	priv.devmode = plat->devmode;
> +
> +	switch (priv.devmode) {
> +	case SIMATIC_IPC_DEVICE_127E:
> +	case SIMATIC_IPC_DEVICE_227G:
> +	case SIMATIC_IPC_DEVICE_BX_39A:
> +	case SIMATIC_IPC_DEVICE_BX_21A:
> +		table->dev_id = dev_name(dev);
> +		gpiod_add_lookup_table(table);
> +		break;
> +	case SIMATIC_IPC_DEVICE_227E:
> +		goto nogpio;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[0])) {
> +		err = PTR_ERR(priv.gpios[0]);
> +		priv.gpios[0] = NULL;
> +		goto out;
> +	}
> +	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[1])) {
> +		err = PTR_ERR(priv.gpios[1]);
> +		priv.gpios[1] = NULL;
> +		goto out;
> +	}
> +
> +	if (table->table[2].key) {
> +		priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
> +		if (IS_ERR(priv.gpios[2])) {
> +			err = PTR_ERR(priv.gpios[1]);
> +			priv.gpios[2] = NULL;
> +			goto out;
> +		}
> +	} else {
> +		priv.gpios[2] = NULL;
> +	}
> +
> +nogpio:
> +	hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME,
> +							 &priv,
> +							 &simatic_ipc_batt_chip_info,
> +							 NULL);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto out;
> +	}
> +
> +	/* warn about aging battery even if userspace never reads hwmon */
> +	simatic_ipc_batt_read_value(dev);
> +
> +	return 0;
> +out:
> +	simatic_ipc_batt_remove(pdev, table);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
> +
> +static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, NULL);
> +}
> +
> +static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, NULL);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_io_probe,
> +	.remove = simatic_ipc_batt_io_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.h b/drivers/platform/x86/simatic-ipc-batt.h
> new file mode 100644
> index 000000000000..4545cd3e3026
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Author:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#ifndef _SIMATIC_IPC_BATT_H
> +#define _SIMATIC_IPC_BATT_H
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev,
> +			   struct gpiod_lookup_table *table);
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev,
> +			    struct gpiod_lookup_table *table);
> +
> +#endif /* _SIMATIC_IPC_BATT_H */
> diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
> index 4402cd354104..6d2c94c17414 100644
> --- a/drivers/platform/x86/simatic-ipc.c
> +++ b/drivers/platform/x86/simatic-ipc.c
> @@ -21,6 +21,7 @@
>  
>  static struct platform_device *ipc_led_platform_device;
>  static struct platform_device *ipc_wdt_platform_device;
> +static struct platform_device *ipc_batt_platform_device;
>  
>  static const struct dmi_system_id simatic_ipc_whitelist[] = {
>  	{
> @@ -37,38 +38,71 @@ static struct {
>  	u32 station_id;
>  	u8 led_mode;
>  	u8 wdt_mode;
> +	u8 batt_mode;
>  } device_modes[] = {
> -	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC127E,
> +		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E},
> +	{SIMATIC_IPC_IPC227D,
> +		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC227E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC227G,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> +	{SIMATIC_IPC_IPC277E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC427D,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC427E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC477E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPCBX_39A,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCPX_39A,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCBX_21A,
> +		SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A},
>  };
>  
>  static int register_platform_devices(u32 station_id)
>  {
>  	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
>  	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
> -	char *pdevname = KBUILD_MODNAME "_leds";
> +	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
> +	char *pdevname;
>  	int i;
>  
> -	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
> -
>  	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
>  		if (device_modes[i].station_id == station_id) {
>  			ledmode = device_modes[i].led_mode;
>  			wdtmode = device_modes[i].wdt_mode;
> +			battmode = device_modes[i].batt_mode;
>  			break;
>  		}
>  	}
>  
> +	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_batt";
> +		if (battmode == SIMATIC_IPC_DEVICE_127E)
> +			pdevname = KBUILD_MODNAME "_batt_apollolake";
> +		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
> +			pdevname = KBUILD_MODNAME "_batt_elkhartlake";
> +		if (battmode == SIMATIC_IPC_DEVICE_227G || battmode == SIMATIC_IPC_DEVICE_BX_39A)
> +			pdevname = KBUILD_MODNAME "_batt_f7188x";
> +		platform_data.devmode = battmode;
> +		ipc_batt_platform_device =
> +			platform_device_register_data(NULL, pdevname,
> +				PLATFORM_DEVID_NONE, &platform_data,
> +				sizeof(struct simatic_ipc_platform));
> +		if (IS_ERR(ipc_batt_platform_device))
> +			return PTR_ERR(ipc_batt_platform_device);
> +
> +		pr_debug("device=%s created\n",
> +			 ipc_batt_platform_device->name);
> +	}
> +
>  	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_leds";
>  		if (ledmode == SIMATIC_IPC_DEVICE_127E)
>  			pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
>  		if (ledmode == SIMATIC_IPC_DEVICE_227G)
> @@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
>  
>  	platform_device_unregister(ipc_wdt_platform_device);
>  	ipc_wdt_platform_device = NULL;
> +
> +	platform_device_unregister(ipc_batt_platform_device);
> +	ipc_batt_platform_device = NULL;
>  }
>  
>  module_init(simatic_ipc_init_module);
> diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h
> index 00bf18ecb160..1ba47dd6939e 100644
> --- a/include/linux/platform_data/x86/simatic-ipc-base.h
> +++ b/include/linux/platform_data/x86/simatic-ipc-base.h
> @@ -21,6 +21,7 @@
>  #define SIMATIC_IPC_DEVICE_227E 4
>  #define SIMATIC_IPC_DEVICE_227G 5
>  #define SIMATIC_IPC_DEVICE_BX_21A 6
> +#define SIMATIC_IPC_DEVICE_BX_39A 7
>  
>  struct simatic_ipc_platform {
>  	u8	devmode;


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

* Re: [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  2023-07-10 13:33     ` Hans de Goede
@ 2023-07-10 13:45       ` Guenter Roeck
  2023-07-10 18:54         ` Henning Schild
  0 siblings, 1 reply; 14+ messages in thread
From: Guenter Roeck @ 2023-07-10 13:45 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Henning Schild, Mark Gross, linux-kernel, platform-driver-x86,
	Wim Van Sebroeck, Jean Delvare, linux-watchdog, linux-hwmon,
	Tobias Schaffner, Gerd Haeussler

On Mon, Jul 10, 2023 at 03:33:45PM +0200, Hans de Goede wrote:
> Hi Guenter,
> 
> On 7/6/23 18:03, Guenter Roeck wrote:
> > On 7/6/23 08:48, Henning Schild wrote:
> >> The IO region used for the watchdog also hold CMOS battery monitoring
> >> information. Make the access muxed so that a hwmon driver can use the
> >> region as well.
> >>
> >> Signed-off-by: Henning Schild <henning.schild@siemens.com>
> > 
> > Acked-by: Guenter Roeck <linux@roeck-us.net>
> 
> Thank you. Is it ok if I pick up his patch and merge it together with 2/2
> through the pdx86 tree ?
> 

Yes. That was my assumption.

Guenter

> Regards,
> 
> Hans
> 
> 
> > 
> >> ---
> >>   drivers/watchdog/simatic-ipc-wdt.c | 9 ++++++---
> >>   1 file changed, 6 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c
> >> index 6599695dc672..cdc1a2e15180 100644
> >> --- a/drivers/watchdog/simatic-ipc-wdt.c
> >> +++ b/drivers/watchdog/simatic-ipc-wdt.c
> >> @@ -155,9 +155,8 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
> >>         switch (plat->devmode) {
> >>       case SIMATIC_IPC_DEVICE_227E:
> >> -        if (!devm_request_region(dev, gp_status_reg_227e_res.start,
> >> -                     resource_size(&gp_status_reg_227e_res),
> >> -                     KBUILD_MODNAME)) {
> >> +        res = &gp_status_reg_227e_res;
> >> +        if (!request_muxed_region(res->start, resource_size(res), res->name)) {
> >>               dev_err(dev,
> >>                   "Unable to register IO resource at %pR\n",
> >>                   &gp_status_reg_227e_res);
> >> @@ -210,6 +209,10 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev)
> >>       if (wdd_data.bootstatus)
> >>           dev_warn(dev, "last reboot caused by watchdog reset\n");
> >>   +    if (plat->devmode == SIMATIC_IPC_DEVICE_227E)
> >> +        release_region(gp_status_reg_227e_res.start,
> >> +                   resource_size(&gp_status_reg_227e_res));
> >> +
> >>       watchdog_set_nowayout(&wdd_data, nowayout);
> >>       watchdog_stop_on_reboot(&wdd_data);
> >>       return devm_watchdog_register_device(dev, &wdd_data);
> > 
> 

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

* Re: [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed
  2023-07-10 13:45       ` Guenter Roeck
@ 2023-07-10 18:54         ` Henning Schild
  0 siblings, 0 replies; 14+ messages in thread
From: Henning Schild @ 2023-07-10 18:54 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Hans de Goede, Mark Gross, linux-kernel, platform-driver-x86,
	Wim Van Sebroeck, Jean Delvare, linux-watchdog, linux-hwmon,
	Tobias Schaffner, Gerd Haeussler

Am Mon, 10 Jul 2023 06:45:14 -0700
schrieb Guenter Roeck <linux@roeck-us.net>:

> On Mon, Jul 10, 2023 at 03:33:45PM +0200, Hans de Goede wrote:
> > Hi Guenter,
> > 
> > On 7/6/23 18:03, Guenter Roeck wrote:  
> > > On 7/6/23 08:48, Henning Schild wrote:  
> > >> The IO region used for the watchdog also hold CMOS battery
> > >> monitoring information. Make the access muxed so that a hwmon
> > >> driver can use the region as well.
> > >>
> > >> Signed-off-by: Henning Schild <henning.schild@siemens.com>  
> > > 
> > > Acked-by: Guenter Roeck <linux@roeck-us.net>  
> > 
> > Thank you. Is it ok if I pick up his patch and merge it together
> > with 2/2 through the pdx86 tree ?
> >   
> 
> Yes. That was my assumption.

Thanks guys. Cant believe i made it with v1. Getting better at this ;)

Henning

> Guenter
> 
> > Regards,
> > 
> > Hans
> > 
> >   
> > >   
> > >> ---
> > >>   drivers/watchdog/simatic-ipc-wdt.c | 9 ++++++---
> > >>   1 file changed, 6 insertions(+), 3 deletions(-)
> > >>
> > >> diff --git a/drivers/watchdog/simatic-ipc-wdt.c
> > >> b/drivers/watchdog/simatic-ipc-wdt.c index
> > >> 6599695dc672..cdc1a2e15180 100644 ---
> > >> a/drivers/watchdog/simatic-ipc-wdt.c +++
> > >> b/drivers/watchdog/simatic-ipc-wdt.c @@ -155,9 +155,8 @@ static
> > >> int simatic_ipc_wdt_probe(struct platform_device *pdev) switch
> > >> (plat->devmode) { case SIMATIC_IPC_DEVICE_227E:
> > >> -        if (!devm_request_region(dev,
> > >> gp_status_reg_227e_res.start,
> > >> -                     resource_size(&gp_status_reg_227e_res),
> > >> -                     KBUILD_MODNAME)) {
> > >> +        res = &gp_status_reg_227e_res;
> > >> +        if (!request_muxed_region(res->start,
> > >> resource_size(res), res->name)) { dev_err(dev,
> > >>                   "Unable to register IO resource at %pR\n",
> > >>                   &gp_status_reg_227e_res);
> > >> @@ -210,6 +209,10 @@ static int simatic_ipc_wdt_probe(struct
> > >> platform_device *pdev) if (wdd_data.bootstatus)
> > >>           dev_warn(dev, "last reboot caused by watchdog
> > >> reset\n");
> > >>   +    if (plat->devmode == SIMATIC_IPC_DEVICE_227E)
> > >> +        release_region(gp_status_reg_227e_res.start,
> > >> +                   resource_size(&gp_status_reg_227e_res));
> > >> +
> > >>       watchdog_set_nowayout(&wdd_data, nowayout);
> > >>       watchdog_stop_on_reboot(&wdd_data);
> > >>       return devm_watchdog_register_device(dev, &wdd_data);  
> > >   
> >   


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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
  2023-07-10 13:34   ` Hans de Goede
@ 2023-07-12 18:02   ` Hans de Goede
  2023-07-12 18:05   ` Hans de Goede
  2 siblings, 0 replies; 14+ messages in thread
From: Hans de Goede @ 2023-07-12 18:02 UTC (permalink / raw)
  To: Henning Schild, Mark Gross, linux-kernel, platform-driver-x86,
	Lee Jones
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler,
	Linux LED Subsystem

Hi,

On 7/6/23 17:48, Henning Schild wrote:
> Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
> battery with two bits that indicate low or empty state. This can be GPIO
> or PortIO based.
> Here we model that as a hwmon voltage. The core driver does the PortIO
> and provides boilerplate for the GPIO versions. Which are split out to
> model runtime dependencies while allowing fine-grained kernel
> configuration.
> 
> Signed-off-by: Henning Schild <henning.schild@siemens.com>

So I tried to merge this, but it does not apply because:

"[PATCH 1/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/

has not been merged yet.

I think it would be best to split the:

drivers/platform/x86/simatic-ipc.c
include/linux/platform_data/x86/simatic-ipc-base.h
include/linux/platform_data/x86/simatic-ipc.h

bits of https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/

out into its own prep patch named e.g. :
"platform/x86: simatic-ipc: add BX-21A model"

And then post a new v2 series for
"leds: simatic-ipc-leds-gpio: add new model BX-21A"
consisting of the prep patch + the actual new LED driver.

Then I can do an immutable branch based on 6.5-rc1 with
the prep patch in there and send a pull-req to Lee Jones
for that, so that he can apply the LED driver patch on
top of the immutable branch.

This way we can continue with merging all the pending
simatic IPC work without being dependent on Lee having
time to review the LED driver.

Regards,

Hans







> ---
>  drivers/platform/x86/Kconfig                  |  48 ++++
>  drivers/platform/x86/Makefile                 |   6 +-
>  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
>  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
>  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
>  drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
>  drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
>  drivers/platform/x86/simatic-ipc.c            |  65 ++++-
>  .../platform_data/x86/simatic-ipc-base.h      |   1 +
>  9 files changed, 547 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.h
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 49c2c4cd8d00..ad15063e1178 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called simatic-ipc.
>  
> +config SIEMENS_SIMATIC_IPC_BATT
> +	tristate "CMOS battery driver for Siemens Simatic IPCs"
> +	depends on HWMON
> +	depends on SIEMENS_SIMATIC_IPC
> +	default SIEMENS_SIMATIC_IPC
> +	help
> +	  This option enables support for monitoring the voltage of the CMOS
> +	  batteries of several Industrial PCs from Siemens.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO"
> +	depends on PINCTRL_BROXTON
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Apollo Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-apollolake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO"
> +	depends on PINCTRL_ELKHARTLAKE
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Elkhart Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_F7188X
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO"
> +	depends on GPIO_F7188X
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Nuvoton GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
>  config WINMATE_FM07_KEYS
>  	tristate "Winmate FM07/FM07P front-panel keys driver"
>  	depends on INPUT
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 52dfdf574ac2..522da0d1584d 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+= intel_scu_ipcutil.o
>  obj-$(CONFIG_X86_INTEL_LPSS)		+= pmc_atom.o
>  
>  # Siemens Simatic Industrial PCs
> -obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)			+= simatic-ipc-batt.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+= simatic-ipc-batt-apollolake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+= simatic-ipc-batt-elkhartlake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+= simatic-ipc-batt-f7188x.o
>  
>  # Winmate
>  obj-$(CONFIG_WINMATE_FM07_KEYS)		+= winmate-fm07-keys.o
> diff --git a/drivers/platform/x86/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> new file mode 100644
> index 000000000000..0503cb89d8f8
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_apollolake_probe,
> +	.remove = simatic_ipc_batt_apollolake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> new file mode 100644
> index 000000000000..ecf5ceb167f9
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_elkhartlake_probe,
> +	.remove = simatic_ipc_batt_elkhartlake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> new file mode 100644
> index 000000000000..a0189b9289f6
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_f7188x_probe,
> +	.remove = simatic_ipc_batt_f7188x_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.c b/drivers/platform/x86/simatic-ipc-batt.c
> new file mode 100644
> index 000000000000..a83272475b9d
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +#include <linux/sizes.h>
> +
> +#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h delay */
> +
> +#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
> +#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
> +#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
> +
> +static struct simatic_ipc_batt {
> +	u8 devmode;
> +	long current_state;
> +	struct gpio_desc *gpios[3];
> +	unsigned long last_updated_jiffies;
> +} priv;
> +
> +static long simatic_ipc_batt_read_gpio(void)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +
> +	if (priv.gpios[2]) {
> +		gpiod_set_value(priv.gpios[2], 1);
> +		msleep(150);
> +	}
> +
> +	if (gpiod_get_value_cansleep(priv.gpios[0]))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (gpiod_get_value_cansleep(priv.gpios[1]))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	if (priv.gpios[2])
> +		gpiod_set_value(priv.gpios[2], 0);
> +
> +	return r;
> +}
> +
> +#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
> +static struct resource simatic_ipc_batt_io_res =
> +	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME);
> +
> +static long simatic_ipc_batt_read_io(struct device *dev)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +	struct resource *res = &simatic_ipc_batt_io_res;
> +	u8 val;
> +
> +	if (!request_muxed_region(res->start, resource_size(res), res->name)) {
> +		dev_err(dev, "Unable to register IO resource at %pR\n", res);
> +		return -EBUSY;
> +	}
> +
> +	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
> +	release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res));
> +
> +	if (val & (1 << 7))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (val & (1 << 6))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	return r;
> +}
> +
> +static long simatic_ipc_batt_read_value(struct device *dev)
> +{
> +	unsigned long next_update;
> +
> +	next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS);
> +	if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) {
> +		switch (priv.devmode) {
> +		case SIMATIC_IPC_DEVICE_127E:
> +		case SIMATIC_IPC_DEVICE_227G:
> +		case SIMATIC_IPC_DEVICE_BX_39A:
> +			priv.current_state = simatic_ipc_batt_read_gpio();
> +			break;
> +		case SIMATIC_IPC_DEVICE_227E:
> +			priv.current_state = simatic_ipc_batt_read_io(dev);
> +			break;
> +		}
> +		priv.last_updated_jiffies = jiffies;
> +		if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL)
> +			dev_warn(dev, "CMOS battery needs to be replaced.");
> +	}
> +
> +	return priv.current_state;
> +}
> +
> +static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type,
> +				 u32 attr, int channel, long *val)
> +{
> +	switch (attr) {
> +	case hwmon_in_input:
> +		*val = simatic_ipc_batt_read_value(dev);
> +		break;
> +	case hwmon_in_lcrit:
> +		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type,
> +					   u32 attr, int channel)
> +{
> +	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops simatic_ipc_batt_ops = {
> +	.is_visible = simatic_ipc_batt_is_visible,
> +	.read = simatic_ipc_batt_read,
> +};
> +
> +static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
> +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
> +	NULL
> +};
> +
> +static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
> +	.ops = &simatic_ipc_batt_ops,
> +	.info = simatic_ipc_batt_info,
> +};
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	gpiod_remove_lookup_table(table);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	struct simatic_ipc_platform *plat;
> +	struct device *dev = &pdev->dev;
> +	struct device *hwmon_dev;
> +	int err;
> +
> +	plat = pdev->dev.platform_data;
> +	priv.devmode = plat->devmode;
> +
> +	switch (priv.devmode) {
> +	case SIMATIC_IPC_DEVICE_127E:
> +	case SIMATIC_IPC_DEVICE_227G:
> +	case SIMATIC_IPC_DEVICE_BX_39A:
> +	case SIMATIC_IPC_DEVICE_BX_21A:
> +		table->dev_id = dev_name(dev);
> +		gpiod_add_lookup_table(table);
> +		break;
> +	case SIMATIC_IPC_DEVICE_227E:
> +		goto nogpio;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[0])) {
> +		err = PTR_ERR(priv.gpios[0]);
> +		priv.gpios[0] = NULL;
> +		goto out;
> +	}
> +	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[1])) {
> +		err = PTR_ERR(priv.gpios[1]);
> +		priv.gpios[1] = NULL;
> +		goto out;
> +	}
> +
> +	if (table->table[2].key) {
> +		priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
> +		if (IS_ERR(priv.gpios[2])) {
> +			err = PTR_ERR(priv.gpios[1]);
> +			priv.gpios[2] = NULL;
> +			goto out;
> +		}
> +	} else {
> +		priv.gpios[2] = NULL;
> +	}
> +
> +nogpio:
> +	hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME,
> +							 &priv,
> +							 &simatic_ipc_batt_chip_info,
> +							 NULL);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto out;
> +	}
> +
> +	/* warn about aging battery even if userspace never reads hwmon */
> +	simatic_ipc_batt_read_value(dev);
> +
> +	return 0;
> +out:
> +	simatic_ipc_batt_remove(pdev, table);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
> +
> +static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, NULL);
> +}
> +
> +static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, NULL);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_io_probe,
> +	.remove = simatic_ipc_batt_io_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.h b/drivers/platform/x86/simatic-ipc-batt.h
> new file mode 100644
> index 000000000000..4545cd3e3026
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Author:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#ifndef _SIMATIC_IPC_BATT_H
> +#define _SIMATIC_IPC_BATT_H
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev,
> +			   struct gpiod_lookup_table *table);
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev,
> +			    struct gpiod_lookup_table *table);
> +
> +#endif /* _SIMATIC_IPC_BATT_H */
> diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
> index 4402cd354104..6d2c94c17414 100644
> --- a/drivers/platform/x86/simatic-ipc.c
> +++ b/drivers/platform/x86/simatic-ipc.c
> @@ -21,6 +21,7 @@
>  
>  static struct platform_device *ipc_led_platform_device;
>  static struct platform_device *ipc_wdt_platform_device;
> +static struct platform_device *ipc_batt_platform_device;
>  
>  static const struct dmi_system_id simatic_ipc_whitelist[] = {
>  	{
> @@ -37,38 +38,71 @@ static struct {
>  	u32 station_id;
>  	u8 led_mode;
>  	u8 wdt_mode;
> +	u8 batt_mode;
>  } device_modes[] = {
> -	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC127E,
> +		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E},
> +	{SIMATIC_IPC_IPC227D,
> +		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC227E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC227G,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> +	{SIMATIC_IPC_IPC277E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC427D,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC427E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC477E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPCBX_39A,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCPX_39A,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCBX_21A,
> +		SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A},
>  };
>  
>  static int register_platform_devices(u32 station_id)
>  {
>  	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
>  	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
> -	char *pdevname = KBUILD_MODNAME "_leds";
> +	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
> +	char *pdevname;
>  	int i;
>  
> -	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
> -
>  	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
>  		if (device_modes[i].station_id == station_id) {
>  			ledmode = device_modes[i].led_mode;
>  			wdtmode = device_modes[i].wdt_mode;
> +			battmode = device_modes[i].batt_mode;
>  			break;
>  		}
>  	}
>  
> +	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_batt";
> +		if (battmode == SIMATIC_IPC_DEVICE_127E)
> +			pdevname = KBUILD_MODNAME "_batt_apollolake";
> +		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
> +			pdevname = KBUILD_MODNAME "_batt_elkhartlake";
> +		if (battmode == SIMATIC_IPC_DEVICE_227G || battmode == SIMATIC_IPC_DEVICE_BX_39A)
> +			pdevname = KBUILD_MODNAME "_batt_f7188x";
> +		platform_data.devmode = battmode;
> +		ipc_batt_platform_device =
> +			platform_device_register_data(NULL, pdevname,
> +				PLATFORM_DEVID_NONE, &platform_data,
> +				sizeof(struct simatic_ipc_platform));
> +		if (IS_ERR(ipc_batt_platform_device))
> +			return PTR_ERR(ipc_batt_platform_device);
> +
> +		pr_debug("device=%s created\n",
> +			 ipc_batt_platform_device->name);
> +	}
> +
>  	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_leds";
>  		if (ledmode == SIMATIC_IPC_DEVICE_127E)
>  			pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
>  		if (ledmode == SIMATIC_IPC_DEVICE_227G)
> @@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
>  
>  	platform_device_unregister(ipc_wdt_platform_device);
>  	ipc_wdt_platform_device = NULL;
> +
> +	platform_device_unregister(ipc_batt_platform_device);
> +	ipc_batt_platform_device = NULL;
>  }
>  
>  module_init(simatic_ipc_init_module);
> diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h
> index 00bf18ecb160..1ba47dd6939e 100644
> --- a/include/linux/platform_data/x86/simatic-ipc-base.h
> +++ b/include/linux/platform_data/x86/simatic-ipc-base.h
> @@ -21,6 +21,7 @@
>  #define SIMATIC_IPC_DEVICE_227E 4
>  #define SIMATIC_IPC_DEVICE_227G 5
>  #define SIMATIC_IPC_DEVICE_BX_21A 6
> +#define SIMATIC_IPC_DEVICE_BX_39A 7
>  
>  struct simatic_ipc_platform {
>  	u8	devmode;


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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
  2023-07-10 13:34   ` Hans de Goede
  2023-07-12 18:02   ` Hans de Goede
@ 2023-07-12 18:05   ` Hans de Goede
  2023-07-13  8:28     ` Henning Schild
  2023-07-13 11:59     ` Henning Schild
  2 siblings, 2 replies; 14+ messages in thread
From: Hans de Goede @ 2023-07-12 18:05 UTC (permalink / raw)
  To: Henning Schild, Mark Gross, linux-kernel, platform-driver-x86,
	Lee Jones
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler,
	Linux LED Subsystem

<resend with Lee's new email address>

Hi,

On 7/6/23 17:48, Henning Schild wrote:
> Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
> battery with two bits that indicate low or empty state. This can be GPIO
> or PortIO based.
> Here we model that as a hwmon voltage. The core driver does the PortIO
> and provides boilerplate for the GPIO versions. Which are split out to
> model runtime dependencies while allowing fine-grained kernel
> configuration.
> 
> Signed-off-by: Henning Schild <henning.schild@siemens.com>

So I tried to merge this, but it does not apply because:

"[PATCH 1/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/

has not been merged yet.

I think it would be best to split the:

drivers/platform/x86/simatic-ipc.c
include/linux/platform_data/x86/simatic-ipc-base.h
include/linux/platform_data/x86/simatic-ipc.h

bits of https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/

out into its own prep patch named e.g. :
"platform/x86: simatic-ipc: add BX-21A model"

And then post a new v2 series for
"leds: simatic-ipc-leds-gpio: add new model BX-21A"
consisting of the prep patch + the actual new LED driver.

Then I can do an immutable branch based on 6.5-rc1 with
the prep patch in there and send a pull-req to Lee Jones
for that, so that he can apply the LED driver patch on
top of the immutable branch.

This way we can continue with merging all the pending
simatic IPC work without being dependent on Lee having
time to review the LED driver.

Regards,

Hans







> ---
>  drivers/platform/x86/Kconfig                  |  48 ++++
>  drivers/platform/x86/Makefile                 |   6 +-
>  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
>  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
>  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
>  drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
>  drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
>  drivers/platform/x86/simatic-ipc.c            |  65 ++++-
>  .../platform_data/x86/simatic-ipc-base.h      |   1 +
>  9 files changed, 547 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.h
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 49c2c4cd8d00..ad15063e1178 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called simatic-ipc.
>  
> +config SIEMENS_SIMATIC_IPC_BATT
> +	tristate "CMOS battery driver for Siemens Simatic IPCs"
> +	depends on HWMON
> +	depends on SIEMENS_SIMATIC_IPC
> +	default SIEMENS_SIMATIC_IPC
> +	help
> +	  This option enables support for monitoring the voltage of the CMOS
> +	  batteries of several Industrial PCs from Siemens.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO"
> +	depends on PINCTRL_BROXTON
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Apollo Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-apollolake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO"
> +	depends on PINCTRL_ELKHARTLAKE
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Elkhart Lake GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
> +config SIEMENS_SIMATIC_IPC_BATT_F7188X
> +	tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO"
> +	depends on GPIO_F7188X
> +	depends on SIEMENS_SIMATIC_IPC_BATT
> +	default SIEMENS_SIMATIC_IPC_BATT
> +	help
> +	  This option enables CMOS battery monitoring for Simatic Industrial PCs
> +	  from Siemens based on Nuvoton GPIO.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called simatic-ipc-batt-elkhartlake.
> +
>  config WINMATE_FM07_KEYS
>  	tristate "Winmate FM07/FM07P front-panel keys driver"
>  	depends on INPUT
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 52dfdf574ac2..522da0d1584d 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+= intel_scu_ipcutil.o
>  obj-$(CONFIG_X86_INTEL_LPSS)		+= pmc_atom.o
>  
>  # Siemens Simatic Industrial PCs
> -obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+= simatic-ipc.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)			+= simatic-ipc-batt.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+= simatic-ipc-batt-apollolake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+= simatic-ipc-batt-elkhartlake.o
> +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+= simatic-ipc-batt-f7188x.o
>  
>  # Winmate
>  obj-$(CONFIG_WINMATE_FM07_KEYS)		+= winmate-fm07-keys.o
> diff --git a/drivers/platform/x86/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> new file mode 100644
> index 000000000000..0503cb89d8f8
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_apollolake_probe,
> +	.remove = simatic_ipc_batt_apollolake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> new file mode 100644
> index 000000000000..ecf5ceb167f9
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_elkhartlake_probe,
> +	.remove = simatic_ipc_batt_elkhartlake_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> new file mode 100644
> index 000000000000..a0189b9289f6
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> @@ -0,0 +1,70 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +
> +#include "simatic-ipc-batt.h"
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
> +	.table = {
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH),
> +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH),
> +		{} /* Terminating entry */
> +	},
> +};
> +
> +static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev)
> +{
> +	const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
> +
> +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> +		return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_227g);
> +
> +	return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_39a);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_f7188x_probe,
> +	.remove = simatic_ipc_batt_f7188x_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl");
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.c b/drivers/platform/x86/simatic-ipc-batt.c
> new file mode 100644
> index 000000000000..a83272475b9d
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Authors:
> + *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/x86/simatic-ipc-base.h>
> +#include <linux/sizes.h>
> +
> +#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h delay */
> +
> +#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
> +#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
> +#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
> +
> +static struct simatic_ipc_batt {
> +	u8 devmode;
> +	long current_state;
> +	struct gpio_desc *gpios[3];
> +	unsigned long last_updated_jiffies;
> +} priv;
> +
> +static long simatic_ipc_batt_read_gpio(void)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +
> +	if (priv.gpios[2]) {
> +		gpiod_set_value(priv.gpios[2], 1);
> +		msleep(150);
> +	}
> +
> +	if (gpiod_get_value_cansleep(priv.gpios[0]))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (gpiod_get_value_cansleep(priv.gpios[1]))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	if (priv.gpios[2])
> +		gpiod_set_value(priv.gpios[2], 0);
> +
> +	return r;
> +}
> +
> +#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
> +static struct resource simatic_ipc_batt_io_res =
> +	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME);
> +
> +static long simatic_ipc_batt_read_io(struct device *dev)
> +{
> +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> +	struct resource *res = &simatic_ipc_batt_io_res;
> +	u8 val;
> +
> +	if (!request_muxed_region(res->start, resource_size(res), res->name)) {
> +		dev_err(dev, "Unable to register IO resource at %pR\n", res);
> +		return -EBUSY;
> +	}
> +
> +	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
> +	release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res));
> +
> +	if (val & (1 << 7))
> +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> +	else if (val & (1 << 6))
> +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +
> +	return r;
> +}
> +
> +static long simatic_ipc_batt_read_value(struct device *dev)
> +{
> +	unsigned long next_update;
> +
> +	next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS);
> +	if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) {
> +		switch (priv.devmode) {
> +		case SIMATIC_IPC_DEVICE_127E:
> +		case SIMATIC_IPC_DEVICE_227G:
> +		case SIMATIC_IPC_DEVICE_BX_39A:
> +			priv.current_state = simatic_ipc_batt_read_gpio();
> +			break;
> +		case SIMATIC_IPC_DEVICE_227E:
> +			priv.current_state = simatic_ipc_batt_read_io(dev);
> +			break;
> +		}
> +		priv.last_updated_jiffies = jiffies;
> +		if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL)
> +			dev_warn(dev, "CMOS battery needs to be replaced.");
> +	}
> +
> +	return priv.current_state;
> +}
> +
> +static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type,
> +				 u32 attr, int channel, long *val)
> +{
> +	switch (attr) {
> +	case hwmon_in_input:
> +		*val = simatic_ipc_batt_read_value(dev);
> +		break;
> +	case hwmon_in_lcrit:
> +		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type,
> +					   u32 attr, int channel)
> +{
> +	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops simatic_ipc_batt_ops = {
> +	.is_visible = simatic_ipc_batt_is_visible,
> +	.read = simatic_ipc_batt_read,
> +};
> +
> +static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
> +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
> +	NULL
> +};
> +
> +static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
> +	.ops = &simatic_ipc_batt_ops,
> +	.info = simatic_ipc_batt_info,
> +};
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	gpiod_remove_lookup_table(table);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table)
> +{
> +	struct simatic_ipc_platform *plat;
> +	struct device *dev = &pdev->dev;
> +	struct device *hwmon_dev;
> +	int err;
> +
> +	plat = pdev->dev.platform_data;
> +	priv.devmode = plat->devmode;
> +
> +	switch (priv.devmode) {
> +	case SIMATIC_IPC_DEVICE_127E:
> +	case SIMATIC_IPC_DEVICE_227G:
> +	case SIMATIC_IPC_DEVICE_BX_39A:
> +	case SIMATIC_IPC_DEVICE_BX_21A:
> +		table->dev_id = dev_name(dev);
> +		gpiod_add_lookup_table(table);
> +		break;
> +	case SIMATIC_IPC_DEVICE_227E:
> +		goto nogpio;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[0])) {
> +		err = PTR_ERR(priv.gpios[0]);
> +		priv.gpios[0] = NULL;
> +		goto out;
> +	}
> +	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN);
> +	if (IS_ERR(priv.gpios[1])) {
> +		err = PTR_ERR(priv.gpios[1]);
> +		priv.gpios[1] = NULL;
> +		goto out;
> +	}
> +
> +	if (table->table[2].key) {
> +		priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
> +		if (IS_ERR(priv.gpios[2])) {
> +			err = PTR_ERR(priv.gpios[1]);
> +			priv.gpios[2] = NULL;
> +			goto out;
> +		}
> +	} else {
> +		priv.gpios[2] = NULL;
> +	}
> +
> +nogpio:
> +	hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME,
> +							 &priv,
> +							 &simatic_ipc_batt_chip_info,
> +							 NULL);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto out;
> +	}
> +
> +	/* warn about aging battery even if userspace never reads hwmon */
> +	simatic_ipc_batt_read_value(dev);
> +
> +	return 0;
> +out:
> +	simatic_ipc_batt_remove(pdev, table);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
> +
> +static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_remove(pdev, NULL);
> +}
> +
> +static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
> +{
> +	return simatic_ipc_batt_probe(pdev, NULL);
> +}
> +
> +static struct platform_driver simatic_ipc_batt_driver = {
> +	.probe = simatic_ipc_batt_io_probe,
> +	.remove = simatic_ipc_batt_io_remove,
> +	.driver = {
> +		.name = KBUILD_MODNAME,
> +	},
> +};
> +
> +module_platform_driver(simatic_ipc_batt_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> diff --git a/drivers/platform/x86/simatic-ipc-batt.h b/drivers/platform/x86/simatic-ipc-batt.h
> new file mode 100644
> index 000000000000..4545cd3e3026
> --- /dev/null
> +++ b/drivers/platform/x86/simatic-ipc-batt.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> + *
> + * Copyright (c) Siemens AG, 2023
> + *
> + * Author:
> + *  Henning Schild <henning.schild@siemens.com>
> + */
> +
> +#ifndef _SIMATIC_IPC_BATT_H
> +#define _SIMATIC_IPC_BATT_H
> +
> +int simatic_ipc_batt_probe(struct platform_device *pdev,
> +			   struct gpiod_lookup_table *table);
> +
> +int simatic_ipc_batt_remove(struct platform_device *pdev,
> +			    struct gpiod_lookup_table *table);
> +
> +#endif /* _SIMATIC_IPC_BATT_H */
> diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
> index 4402cd354104..6d2c94c17414 100644
> --- a/drivers/platform/x86/simatic-ipc.c
> +++ b/drivers/platform/x86/simatic-ipc.c
> @@ -21,6 +21,7 @@
>  
>  static struct platform_device *ipc_led_platform_device;
>  static struct platform_device *ipc_wdt_platform_device;
> +static struct platform_device *ipc_batt_platform_device;
>  
>  static const struct dmi_system_id simatic_ipc_whitelist[] = {
>  	{
> @@ -37,38 +38,71 @@ static struct {
>  	u32 station_id;
>  	u8 led_mode;
>  	u8 wdt_mode;
> +	u8 batt_mode;
>  } device_modes[] = {
> -	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
> -	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> -	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
> -	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
> -	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC127E,
> +		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E},
> +	{SIMATIC_IPC_IPC227D,
> +		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC227E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC227G,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
> +	{SIMATIC_IPC_IPC277E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E},
> +	{SIMATIC_IPC_IPC427D,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC427E,
> +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPC477E,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
> +	{SIMATIC_IPC_IPCBX_39A,
> +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCPX_39A,
> +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_BX_39A},
> +	{SIMATIC_IPC_IPCBX_21A,
> +		SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A},
>  };
>  
>  static int register_platform_devices(u32 station_id)
>  {
>  	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
>  	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
> -	char *pdevname = KBUILD_MODNAME "_leds";
> +	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
> +	char *pdevname;
>  	int i;
>  
> -	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
> -
>  	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
>  		if (device_modes[i].station_id == station_id) {
>  			ledmode = device_modes[i].led_mode;
>  			wdtmode = device_modes[i].wdt_mode;
> +			battmode = device_modes[i].batt_mode;
>  			break;
>  		}
>  	}
>  
> +	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_batt";
> +		if (battmode == SIMATIC_IPC_DEVICE_127E)
> +			pdevname = KBUILD_MODNAME "_batt_apollolake";
> +		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
> +			pdevname = KBUILD_MODNAME "_batt_elkhartlake";
> +		if (battmode == SIMATIC_IPC_DEVICE_227G || battmode == SIMATIC_IPC_DEVICE_BX_39A)
> +			pdevname = KBUILD_MODNAME "_batt_f7188x";
> +		platform_data.devmode = battmode;
> +		ipc_batt_platform_device =
> +			platform_device_register_data(NULL, pdevname,
> +				PLATFORM_DEVID_NONE, &platform_data,
> +				sizeof(struct simatic_ipc_platform));
> +		if (IS_ERR(ipc_batt_platform_device))
> +			return PTR_ERR(ipc_batt_platform_device);
> +
> +		pr_debug("device=%s created\n",
> +			 ipc_batt_platform_device->name);
> +	}
> +
>  	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
> +		pdevname = KBUILD_MODNAME "_leds";
>  		if (ledmode == SIMATIC_IPC_DEVICE_127E)
>  			pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
>  		if (ledmode == SIMATIC_IPC_DEVICE_227G)
> @@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
>  
>  	platform_device_unregister(ipc_wdt_platform_device);
>  	ipc_wdt_platform_device = NULL;
> +
> +	platform_device_unregister(ipc_batt_platform_device);
> +	ipc_batt_platform_device = NULL;
>  }
>  
>  module_init(simatic_ipc_init_module);
> diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h
> index 00bf18ecb160..1ba47dd6939e 100644
> --- a/include/linux/platform_data/x86/simatic-ipc-base.h
> +++ b/include/linux/platform_data/x86/simatic-ipc-base.h
> @@ -21,6 +21,7 @@
>  #define SIMATIC_IPC_DEVICE_227E 4
>  #define SIMATIC_IPC_DEVICE_227G 5
>  #define SIMATIC_IPC_DEVICE_BX_21A 6
> +#define SIMATIC_IPC_DEVICE_BX_39A 7
>  
>  struct simatic_ipc_platform {
>  	u8	devmode;



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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-12 18:05   ` Hans de Goede
@ 2023-07-13  8:28     ` Henning Schild
  2023-07-13  8:36       ` Hans de Goede
  2023-07-13 11:59     ` Henning Schild
  1 sibling, 1 reply; 14+ messages in thread
From: Henning Schild @ 2023-07-13  8:28 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Mark Gross, linux-kernel, platform-driver-x86, Lee Jones,
	Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler,
	Linux LED Subsystem

Am Wed, 12 Jul 2023 20:05:54 +0200
schrieb Hans de Goede <hdegoede@redhat.com>:

> <resend with Lee's new email address>
> 
> Hi,
> 
> On 7/6/23 17:48, Henning Schild wrote:
> > Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
> > battery with two bits that indicate low or empty state. This can be
> > GPIO or PortIO based.
> > Here we model that as a hwmon voltage. The core driver does the
> > PortIO and provides boilerplate for the GPIO versions. Which are
> > split out to model runtime dependencies while allowing fine-grained
> > kernel configuration.
> > 
> > Signed-off-by: Henning Schild <henning.schild@siemens.com>  
> 
> So I tried to merge this, but it does not apply because:
> 
> "[PATCH 1/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
> 
> has not been merged yet.
> 
> I think it would be best to split the:
> 
> drivers/platform/x86/simatic-ipc.c
> include/linux/platform_data/x86/simatic-ipc-base.h
> include/linux/platform_data/x86/simatic-ipc.h
> 
> bits of
> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
> 
> out into its own prep patch named e.g. :
> "platform/x86: simatic-ipc: add BX-21A model"
> 
> And then post a new v2 series for
> "leds: simatic-ipc-leds-gpio: add new model BX-21A"
> consisting of the prep patch + the actual new LED driver.
> 
> Then I can do an immutable branch based on 6.5-rc1 with
> the prep patch in there and send a pull-req to Lee Jones
> for that, so that he can apply the LED driver patch on
> top of the immutable branch.
> 
> This way we can continue with merging all the pending
> simatic IPC work without being dependent on Lee having
> time to review the LED driver.

Sounds like a plan, i will send another version of "leds:
simatic-ipc-leds-gpio: add new model BX-21A" with the pdx86 stuff as
separate patch.

But just to note one thing, we would be registering a platform device
before there is a driver for it. ("simatic_ipc_leds_gpio_elkhartlake")
I think that is fine, but would want to be sure that Lee will actually
merge that driver also for 6.5 so the gap between device and driver
does not become too big.

regards,
Henning

> Regards,
> 
> Hans
> 
> 
> 
> 
> 
> 
> 
> > ---
> >  drivers/platform/x86/Kconfig                  |  48 ++++
> >  drivers/platform/x86/Makefile                 |   6 +-
> >  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
> >  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
> >  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
> >  drivers/platform/x86/simatic-ipc-batt.c       | 250
> > ++++++++++++++++++ drivers/platform/x86/simatic-ipc-batt.h       |
> > 20 ++ drivers/platform/x86/simatic-ipc.c            |  65 ++++-
> >  .../platform_data/x86/simatic-ipc-base.h      |   1 +
> >  9 files changed, 547 insertions(+), 15 deletions(-)
> >  create mode 100644
> > drivers/platform/x86/simatic-ipc-batt-apollolake.c create mode
> > 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c create
> > mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c create
> > mode 100644 drivers/platform/x86/simatic-ipc-batt.c create mode
> > 100644 drivers/platform/x86/simatic-ipc-batt.h
> > 
> > diff --git a/drivers/platform/x86/Kconfig
> > b/drivers/platform/x86/Kconfig index 49c2c4cd8d00..ad15063e1178
> > 100644 --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
> >  	  To compile this driver as a module, choose M here: the
> > module will be called simatic-ipc.
> >  
> > +config SIEMENS_SIMATIC_IPC_BATT
> > +	tristate "CMOS battery driver for Siemens Simatic IPCs"
> > +	depends on HWMON
> > +	depends on SIEMENS_SIMATIC_IPC
> > +	default SIEMENS_SIMATIC_IPC
> > +	help
> > +	  This option enables support for monitoring the voltage
> > of the CMOS
> > +	  batteries of several Industrial PCs from Siemens.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Apollo Lake GPIO"
> > +	depends on PINCTRL_BROXTON
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Apollo Lake GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-apollolake.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Elkhart Lake GPIO"
> > +	depends on PINCTRL_ELKHARTLAKE
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Elkhart Lake GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-elkhartlake.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_F7188X
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Nuvoton GPIO"
> > +	depends on GPIO_F7188X
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Nuvoton GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-elkhartlake.
> > +
> >  config WINMATE_FM07_KEYS
> >  	tristate "Winmate FM07/FM07P front-panel keys driver"
> >  	depends on INPUT
> > diff --git a/drivers/platform/x86/Makefile
> > b/drivers/platform/x86/Makefile index 52dfdf574ac2..522da0d1584d
> > 100644 --- a/drivers/platform/x86/Makefile
> > +++ b/drivers/platform/x86/Makefile
> > @@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+=
> > intel_scu_ipcutil.o obj-$(CONFIG_X86_INTEL_LPSS)		+=
> > pmc_atom.o 
> >  # Siemens Simatic Industrial PCs
> > -obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+=
> > simatic-ipc.o +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)
> > 		+= simatic-ipc-batt.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+=
> > simatic-ipc-batt-apollolake.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+=
> > simatic-ipc-batt-elkhartlake.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+=
> > simatic-ipc-batt-f7188x.o # Winmate
> > obj-$(CONFIG_WINMATE_FM07_KEYS)		+=
> > winmate-fm07-keys.o diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> > b/drivers/platform/x86/simatic-ipc-batt-apollolake.c new file mode
> > 100644 index 000000000000..0503cb89d8f8 --- /dev/null +++
> > b/drivers/platform/x86/simatic-ipc-batt-apollolake.c @@ -0,0 +1,51
> > @@ +// SPDX-License-Identifier: GPL-2.0 +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL,
> > 0, GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL,
> > 1, GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL,
> > 2, GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_apollolake_remove(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_127e); +}
> > +
> > +static int simatic_ipc_batt_apollolake_probe(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_127e); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_apollolake_probe,
> > +	.remove = simatic_ipc_batt_apollolake_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt
> > platform:apollolake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> > b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c new file mode
> > 100644 index 000000000000..ecf5ceb167f9 --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> > @@ -0,0 +1,51 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_elkhartlake_remove(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_bx_21a); +}
> > +
> > +static int simatic_ipc_batt_elkhartlake_probe(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_bx_21a); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_elkhartlake_probe,
> > +	.remove = simatic_ipc_batt_elkhartlake_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt
> > platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> > b/drivers/platform/x86/simatic-ipc-batt-f7188x.c new file mode
> > 100644 index 000000000000..a0189b9289f6 --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> > @@ -0,0 +1,70 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/x86/simatic-ipc-base.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_f7188x_remove(struct platform_device
> > *pdev) +{
> > +	const struct simatic_ipc_platform *plat =
> > pdev->dev.platform_data; +
> > +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> > +		return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_227g); +
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_bx_39a); +}
> > +
> > +static int simatic_ipc_batt_f7188x_probe(struct platform_device
> > *pdev) +{
> > +	const struct simatic_ipc_platform *plat =
> > pdev->dev.platform_data; +
> > +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> > +		return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_227g); +
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_bx_39a); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_f7188x_probe,
> > +	.remove = simatic_ipc_batt_f7188x_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x
> > platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt.c
> > b/drivers/platform/x86/simatic-ipc-batt.c new file mode 100644
> > index 000000000000..a83272475b9d --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt.c
> > @@ -0,0 +1,250 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/ioport.h>
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/jiffies.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/x86/simatic-ipc-base.h>
> > +#include <linux/sizes.h>
> > +
> > +#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h
> > delay */ +
> > +#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
> > +#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
> > +#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
> > +
> > +static struct simatic_ipc_batt {
> > +	u8 devmode;
> > +	long current_state;
> > +	struct gpio_desc *gpios[3];
> > +	unsigned long last_updated_jiffies;
> > +} priv;
> > +
> > +static long simatic_ipc_batt_read_gpio(void)
> > +{
> > +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> > +
> > +	if (priv.gpios[2]) {
> > +		gpiod_set_value(priv.gpios[2], 1);
> > +		msleep(150);
> > +	}
> > +
> > +	if (gpiod_get_value_cansleep(priv.gpios[0]))
> > +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> > +	else if (gpiod_get_value_cansleep(priv.gpios[1]))
> > +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +
> > +	if (priv.gpios[2])
> > +		gpiod_set_value(priv.gpios[2], 0);
> > +
> > +	return r;
> > +}
> > +
> > +#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
> > +static struct resource simatic_ipc_batt_io_res =
> > +	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1,
> > KBUILD_MODNAME); +
> > +static long simatic_ipc_batt_read_io(struct device *dev)
> > +{
> > +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> > +	struct resource *res = &simatic_ipc_batt_io_res;
> > +	u8 val;
> > +
> > +	if (!request_muxed_region(res->start, resource_size(res),
> > res->name)) {
> > +		dev_err(dev, "Unable to register IO resource at
> > %pR\n", res);
> > +		return -EBUSY;
> > +	}
> > +
> > +	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
> > +	release_region(simatic_ipc_batt_io_res.start,
> > resource_size(&simatic_ipc_batt_io_res)); +
> > +	if (val & (1 << 7))
> > +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> > +	else if (val & (1 << 6))
> > +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +
> > +	return r;
> > +}
> > +
> > +static long simatic_ipc_batt_read_value(struct device *dev)
> > +{
> > +	unsigned long next_update;
> > +
> > +	next_update = priv.last_updated_jiffies +
> > msecs_to_jiffies(BATT_DELAY_MS);
> > +	if (time_after(jiffies, next_update) ||
> > !priv.last_updated_jiffies) {
> > +		switch (priv.devmode) {
> > +		case SIMATIC_IPC_DEVICE_127E:
> > +		case SIMATIC_IPC_DEVICE_227G:
> > +		case SIMATIC_IPC_DEVICE_BX_39A:
> > +			priv.current_state =
> > simatic_ipc_batt_read_gpio();
> > +			break;
> > +		case SIMATIC_IPC_DEVICE_227E:
> > +			priv.current_state =
> > simatic_ipc_batt_read_io(dev);
> > +			break;
> > +		}
> > +		priv.last_updated_jiffies = jiffies;
> > +		if (priv.current_state <
> > SIMATIC_IPC_BATT_LEVEL_FULL)
> > +			dev_warn(dev, "CMOS battery needs to be
> > replaced.");
> > +	}
> > +
> > +	return priv.current_state;
> > +}
> > +
> > +static int simatic_ipc_batt_read(struct device *dev, enum
> > hwmon_sensor_types type,
> > +				 u32 attr, int channel, long *val)
> > +{
> > +	switch (attr) {
> > +	case hwmon_in_input:
> > +		*val = simatic_ipc_batt_read_value(dev);
> > +		break;
> > +	case hwmon_in_lcrit:
> > +		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static umode_t simatic_ipc_batt_is_visible(const void *data, enum
> > hwmon_sensor_types type,
> > +					   u32 attr, int channel)
> > +{
> > +	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
> > +		return 0444;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct hwmon_ops simatic_ipc_batt_ops = {
> > +	.is_visible = simatic_ipc_batt_is_visible,
> > +	.read = simatic_ipc_batt_read,
> > +};
> > +
> > +static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
> > +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
> > +	NULL
> > +};
> > +
> > +static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
> > +	.ops = &simatic_ipc_batt_ops,
> > +	.info = simatic_ipc_batt_info,
> > +};
> > +
> > +int simatic_ipc_batt_remove(struct platform_device *pdev, struct
> > gpiod_lookup_table *table) +{
> > +	gpiod_remove_lookup_table(table);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
> > +
> > +int simatic_ipc_batt_probe(struct platform_device *pdev, struct
> > gpiod_lookup_table *table) +{
> > +	struct simatic_ipc_platform *plat;
> > +	struct device *dev = &pdev->dev;
> > +	struct device *hwmon_dev;
> > +	int err;
> > +
> > +	plat = pdev->dev.platform_data;
> > +	priv.devmode = plat->devmode;
> > +
> > +	switch (priv.devmode) {
> > +	case SIMATIC_IPC_DEVICE_127E:
> > +	case SIMATIC_IPC_DEVICE_227G:
> > +	case SIMATIC_IPC_DEVICE_BX_39A:
> > +	case SIMATIC_IPC_DEVICE_BX_21A:
> > +		table->dev_id = dev_name(dev);
> > +		gpiod_add_lookup_table(table);
> > +		break;
> > +	case SIMATIC_IPC_DEVICE_227E:
> > +		goto nogpio;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +
> > +	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery
> > empty", 0, GPIOD_IN);
> > +	if (IS_ERR(priv.gpios[0])) {
> > +		err = PTR_ERR(priv.gpios[0]);
> > +		priv.gpios[0] = NULL;
> > +		goto out;
> > +	}
> > +	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery
> > low", 1, GPIOD_IN);
> > +	if (IS_ERR(priv.gpios[1])) {
> > +		err = PTR_ERR(priv.gpios[1]);
> > +		priv.gpios[1] = NULL;
> > +		goto out;
> > +	}
> > +
> > +	if (table->table[2].key) {
> > +		priv.gpios[2] = devm_gpiod_get_index(dev,
> > "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
> > +		if (IS_ERR(priv.gpios[2])) {
> > +			err = PTR_ERR(priv.gpios[1]);
> > +			priv.gpios[2] = NULL;
> > +			goto out;
> > +		}
> > +	} else {
> > +		priv.gpios[2] = NULL;
> > +	}
> > +
> > +nogpio:
> > +	hwmon_dev = devm_hwmon_device_register_with_info(dev,
> > KBUILD_MODNAME,
> > +							 &priv,
> > +
> > &simatic_ipc_batt_chip_info,
> > +							 NULL);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto out;
> > +	}
> > +
> > +	/* warn about aging battery even if userspace never reads
> > hwmon */
> > +	simatic_ipc_batt_read_value(dev);
> > +
> > +	return 0;
> > +out:
> > +	simatic_ipc_batt_remove(pdev, table);
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
> > +
> > +static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
> > +{
> > +	return simatic_ipc_batt_remove(pdev, NULL);
> > +}
> > +
> > +static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
> > +{
> > +	return simatic_ipc_batt_probe(pdev, NULL);
> > +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_io_probe,
> > +	.remove = simatic_ipc_batt_io_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> > diff --git a/drivers/platform/x86/simatic-ipc-batt.h
> > b/drivers/platform/x86/simatic-ipc-batt.h new file mode 100644
> > index 000000000000..4545cd3e3026
> > --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt.h
> > @@ -0,0 +1,20 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Author:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#ifndef _SIMATIC_IPC_BATT_H
> > +#define _SIMATIC_IPC_BATT_H
> > +
> > +int simatic_ipc_batt_probe(struct platform_device *pdev,
> > +			   struct gpiod_lookup_table *table);
> > +
> > +int simatic_ipc_batt_remove(struct platform_device *pdev,
> > +			    struct gpiod_lookup_table *table);
> > +
> > +#endif /* _SIMATIC_IPC_BATT_H */
> > diff --git a/drivers/platform/x86/simatic-ipc.c
> > b/drivers/platform/x86/simatic-ipc.c index
> > 4402cd354104..6d2c94c17414 100644 ---
> > a/drivers/platform/x86/simatic-ipc.c +++
> > b/drivers/platform/x86/simatic-ipc.c @@ -21,6 +21,7 @@
> >  
> >  static struct platform_device *ipc_led_platform_device;
> >  static struct platform_device *ipc_wdt_platform_device;
> > +static struct platform_device *ipc_batt_platform_device;
> >  
> >  static const struct dmi_system_id simatic_ipc_whitelist[] = {
> >  	{
> > @@ -37,38 +38,71 @@ static struct {
> >  	u32 station_id;
> >  	u8 led_mode;
> >  	u8 wdt_mode;
> > +	u8 batt_mode;
> >  } device_modes[] = {
> > -	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_227E},
> > -	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_227E},
> > -	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_427E},
> > -	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_427E},
> > -	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC127E,
> > +		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_127E},
> > +	{SIMATIC_IPC_IPC227D,
> > +		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC227E,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E,
> > SIMATIC_IPC_DEVICE_227E},
> > +	{SIMATIC_IPC_IPC227G,
> > +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > +	{SIMATIC_IPC_IPC277E,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E,
> > SIMATIC_IPC_DEVICE_227E},
> > +	{SIMATIC_IPC_IPC427D,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC427E,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC477E,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPCBX_39A,
> > +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_BX_39A},
> > +	{SIMATIC_IPC_IPCPX_39A,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_BX_39A},
> > +	{SIMATIC_IPC_IPCBX_21A,
> > +		SIMATIC_IPC_DEVICE_BX_21A,
> > SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A}, };
> >  
> >  static int register_platform_devices(u32 station_id)
> >  {
> >  	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
> >  	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
> > -	char *pdevname = KBUILD_MODNAME "_leds";
> > +	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
> > +	char *pdevname;
> >  	int i;
> >  
> > -	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
> > -
> >  	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
> >  		if (device_modes[i].station_id == station_id) {
> >  			ledmode = device_modes[i].led_mode;
> >  			wdtmode = device_modes[i].wdt_mode;
> > +			battmode = device_modes[i].batt_mode;
> >  			break;
> >  		}
> >  	}
> >  
> > +	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
> > +		pdevname = KBUILD_MODNAME "_batt";
> > +		if (battmode == SIMATIC_IPC_DEVICE_127E)
> > +			pdevname = KBUILD_MODNAME
> > "_batt_apollolake";
> > +		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
> > +			pdevname = KBUILD_MODNAME
> > "_batt_elkhartlake";
> > +		if (battmode == SIMATIC_IPC_DEVICE_227G ||
> > battmode == SIMATIC_IPC_DEVICE_BX_39A)
> > +			pdevname = KBUILD_MODNAME "_batt_f7188x";
> > +		platform_data.devmode = battmode;
> > +		ipc_batt_platform_device =
> > +			platform_device_register_data(NULL,
> > pdevname,
> > +				PLATFORM_DEVID_NONE,
> > &platform_data,
> > +				sizeof(struct
> > simatic_ipc_platform));
> > +		if (IS_ERR(ipc_batt_platform_device))
> > +			return PTR_ERR(ipc_batt_platform_device);
> > +
> > +		pr_debug("device=%s created\n",
> > +			 ipc_batt_platform_device->name);
> > +	}
> > +
> >  	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
> > +		pdevname = KBUILD_MODNAME "_leds";
> >  		if (ledmode == SIMATIC_IPC_DEVICE_127E)
> >  			pdevname = KBUILD_MODNAME
> > "_leds_gpio_apollolake"; if (ledmode == SIMATIC_IPC_DEVICE_227G)
> > @@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
> >  
> >  	platform_device_unregister(ipc_wdt_platform_device);
> >  	ipc_wdt_platform_device = NULL;
> > +
> > +	platform_device_unregister(ipc_batt_platform_device);
> > +	ipc_batt_platform_device = NULL;
> >  }
> >  
> >  module_init(simatic_ipc_init_module);
> > diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h
> > b/include/linux/platform_data/x86/simatic-ipc-base.h index
> > 00bf18ecb160..1ba47dd6939e 100644 ---
> > a/include/linux/platform_data/x86/simatic-ipc-base.h +++
> > b/include/linux/platform_data/x86/simatic-ipc-base.h @@ -21,6 +21,7
> > @@ #define SIMATIC_IPC_DEVICE_227E 4
> >  #define SIMATIC_IPC_DEVICE_227G 5
> >  #define SIMATIC_IPC_DEVICE_BX_21A 6
> > +#define SIMATIC_IPC_DEVICE_BX_39A 7
> >  
> >  struct simatic_ipc_platform {
> >  	u8	devmode;  
> 
> 


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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-13  8:28     ` Henning Schild
@ 2023-07-13  8:36       ` Hans de Goede
  0 siblings, 0 replies; 14+ messages in thread
From: Hans de Goede @ 2023-07-13  8:36 UTC (permalink / raw)
  To: Henning Schild
  Cc: Mark Gross, linux-kernel, platform-driver-x86, Lee Jones,
	Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler,
	Linux LED Subsystem

Hi,

On 7/13/23 10:28, Henning Schild wrote:
> Am Wed, 12 Jul 2023 20:05:54 +0200
> schrieb Hans de Goede <hdegoede@redhat.com>:
> 
>> <resend with Lee's new email address>
>>
>> Hi,
>>
>> On 7/6/23 17:48, Henning Schild wrote:
>>> Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
>>> battery with two bits that indicate low or empty state. This can be
>>> GPIO or PortIO based.
>>> Here we model that as a hwmon voltage. The core driver does the
>>> PortIO and provides boilerplate for the GPIO versions. Which are
>>> split out to model runtime dependencies while allowing fine-grained
>>> kernel configuration.
>>>
>>> Signed-off-by: Henning Schild <henning.schild@siemens.com>  
>>
>> So I tried to merge this, but it does not apply because:
>>
>> "[PATCH 1/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
>> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
>>
>> has not been merged yet.
>>
>> I think it would be best to split the:
>>
>> drivers/platform/x86/simatic-ipc.c
>> include/linux/platform_data/x86/simatic-ipc-base.h
>> include/linux/platform_data/x86/simatic-ipc.h
>>
>> bits of
>> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
>>
>> out into its own prep patch named e.g. :
>> "platform/x86: simatic-ipc: add BX-21A model"
>>
>> And then post a new v2 series for
>> "leds: simatic-ipc-leds-gpio: add new model BX-21A"
>> consisting of the prep patch + the actual new LED driver.
>>
>> Then I can do an immutable branch based on 6.5-rc1 with
>> the prep patch in there and send a pull-req to Lee Jones
>> for that, so that he can apply the LED driver patch on
>> top of the immutable branch.
>>
>> This way we can continue with merging all the pending
>> simatic IPC work without being dependent on Lee having
>> time to review the LED driver.
> 
> Sounds like a plan, i will send another version of "leds:
> simatic-ipc-leds-gpio: add new model BX-21A" with the pdx86 stuff as
> separate patch.

Great.

> But just to note one thing, we would be registering a platform device
> before there is a driver for it. ("simatic_ipc_leds_gpio_elkhartlake")
> I think that is fine>

Yes that really is not an issue, e.g. the ACPI subsystem registers
a platform device for every device in the ACPI tables and not nearly
all of them has a driver. Having a platform_device without a driver
around is not an issue.

Regards,

Hans



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

* Re: [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-12 18:05   ` Hans de Goede
  2023-07-13  8:28     ` Henning Schild
@ 2023-07-13 11:59     ` Henning Schild
  1 sibling, 0 replies; 14+ messages in thread
From: Henning Schild @ 2023-07-13 11:59 UTC (permalink / raw)
  To: Hans de Goede, Lee Jones
  Cc: Mark Gross, linux-kernel, platform-driver-x86, Wim Van Sebroeck,
	Guenter Roeck, Jean Delvare, linux-watchdog, linux-hwmon,
	Tobias Schaffner, Gerd Haeussler, Linux LED Subsystem

Am Wed, 12 Jul 2023 20:05:54 +0200
schrieb Hans de Goede <hdegoede@redhat.com>:

> <resend with Lee's new email address>
> 
> Hi,
> 
> On 7/6/23 17:48, Henning Schild wrote:
> > Siemens Simatic Industrial PCs can monitor the voltage of the CMOS
> > battery with two bits that indicate low or empty state. This can be
> > GPIO or PortIO based.
> > Here we model that as a hwmon voltage. The core driver does the
> > PortIO and provides boilerplate for the GPIO versions. Which are
> > split out to model runtime dependencies while allowing fine-grained
> > kernel configuration.
> > 
> > Signed-off-by: Henning Schild <henning.schild@siemens.com>  
> 
> So I tried to merge this, but it does not apply because:
> 
> "[PATCH 1/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
> 
> has not been merged yet.
> 
> I think it would be best to split the:
> 
> drivers/platform/x86/simatic-ipc.c
> include/linux/platform_data/x86/simatic-ipc-base.h
> include/linux/platform_data/x86/simatic-ipc.h
> 
> bits of
> https://lore.kernel.org/platform-driver-x86/20230531155457.31632-2-henning.schild@siemens.com/
> 
> out into its own prep patch named e.g. :
> "platform/x86: simatic-ipc: add BX-21A model"

just sent
[PATCH v2 1/2] platform/x86: simatic-ipc: add another model BX-21A

which you can base this on and Lee takes v2 2/2 on top

Henning

> And then post a new v2 series for
> "leds: simatic-ipc-leds-gpio: add new model BX-21A"
> consisting of the prep patch + the actual new LED driver.
> 
> Then I can do an immutable branch based on 6.5-rc1 with
> the prep patch in there and send a pull-req to Lee Jones
> for that, so that he can apply the LED driver patch on
> top of the immutable branch.
> 
> This way we can continue with merging all the pending
> simatic IPC work without being dependent on Lee having
> time to review the LED driver.
> 
> Regards,
> 
> Hans
> 
> 
> 
> 
> 
> 
> 
> > ---
> >  drivers/platform/x86/Kconfig                  |  48 ++++
> >  drivers/platform/x86/Makefile                 |   6 +-
> >  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
> >  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
> >  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
> >  drivers/platform/x86/simatic-ipc-batt.c       | 250
> > ++++++++++++++++++ drivers/platform/x86/simatic-ipc-batt.h       |
> > 20 ++ drivers/platform/x86/simatic-ipc.c            |  65 ++++-
> >  .../platform_data/x86/simatic-ipc-base.h      |   1 +
> >  9 files changed, 547 insertions(+), 15 deletions(-)
> >  create mode 100644
> > drivers/platform/x86/simatic-ipc-batt-apollolake.c create mode
> > 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c create
> > mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c create
> > mode 100644 drivers/platform/x86/simatic-ipc-batt.c create mode
> > 100644 drivers/platform/x86/simatic-ipc-batt.h
> > 
> > diff --git a/drivers/platform/x86/Kconfig
> > b/drivers/platform/x86/Kconfig index 49c2c4cd8d00..ad15063e1178
> > 100644 --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -1086,6 +1086,54 @@ config SIEMENS_SIMATIC_IPC
> >  	  To compile this driver as a module, choose M here: the
> > module will be called simatic-ipc.
> >  
> > +config SIEMENS_SIMATIC_IPC_BATT
> > +	tristate "CMOS battery driver for Siemens Simatic IPCs"
> > +	depends on HWMON
> > +	depends on SIEMENS_SIMATIC_IPC
> > +	default SIEMENS_SIMATIC_IPC
> > +	help
> > +	  This option enables support for monitoring the voltage
> > of the CMOS
> > +	  batteries of several Industrial PCs from Siemens.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Apollo Lake GPIO"
> > +	depends on PINCTRL_BROXTON
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Apollo Lake GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-apollolake.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Elkhart Lake GPIO"
> > +	depends on PINCTRL_ELKHARTLAKE
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Elkhart Lake GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-elkhartlake.
> > +
> > +config SIEMENS_SIMATIC_IPC_BATT_F7188X
> > +	tristate "CMOS Battery monitoring for Simatic IPCs based
> > on Nuvoton GPIO"
> > +	depends on GPIO_F7188X
> > +	depends on SIEMENS_SIMATIC_IPC_BATT
> > +	default SIEMENS_SIMATIC_IPC_BATT
> > +	help
> > +	  This option enables CMOS battery monitoring for Simatic
> > Industrial PCs
> > +	  from Siemens based on Nuvoton GPIO.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > module
> > +	  will be called simatic-ipc-batt-elkhartlake.
> > +
> >  config WINMATE_FM07_KEYS
> >  	tristate "Winmate FM07/FM07P front-panel keys driver"
> >  	depends on INPUT
> > diff --git a/drivers/platform/x86/Makefile
> > b/drivers/platform/x86/Makefile index 52dfdf574ac2..522da0d1584d
> > 100644 --- a/drivers/platform/x86/Makefile
> > +++ b/drivers/platform/x86/Makefile
> > @@ -131,7 +131,11 @@ obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+=
> > intel_scu_ipcutil.o obj-$(CONFIG_X86_INTEL_LPSS)		+=
> > pmc_atom.o 
> >  # Siemens Simatic Industrial PCs
> > -obj-$(CONFIG_SIEMENS_SIMATIC_IPC)	+= simatic-ipc.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC)			+=
> > simatic-ipc.o +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT)
> > 		+= simatic-ipc-batt.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE)	+=
> > simatic-ipc-batt-apollolake.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE)	+=
> > simatic-ipc-batt-elkhartlake.o
> > +obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X)		+=
> > simatic-ipc-batt-f7188x.o # Winmate
> > obj-$(CONFIG_WINMATE_FM07_KEYS)		+=
> > winmate-fm07-keys.o diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-apollolake.c
> > b/drivers/platform/x86/simatic-ipc-batt-apollolake.c new file mode
> > 100644 index 000000000000..0503cb89d8f8 --- /dev/null +++
> > b/drivers/platform/x86/simatic-ipc-batt-apollolake.c @@ -0,0 +1,51
> > @@ +// SPDX-License-Identifier: GPL-2.0 +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL,
> > 0, GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL,
> > 1, GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL,
> > 2, GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_apollolake_remove(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_127e); +}
> > +
> > +static int simatic_ipc_batt_apollolake_probe(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_127e); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_apollolake_probe,
> > +	.remove = simatic_ipc_batt_apollolake_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt
> > platform:apollolake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> > b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c new file mode
> > 100644 index 000000000000..ecf5ceb167f9 --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
> > @@ -0,0 +1,51 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_elkhartlake_remove(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_bx_21a); +}
> > +
> > +static int simatic_ipc_batt_elkhartlake_probe(struct
> > platform_device *pdev) +{
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_bx_21a); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_elkhartlake_probe,
> > +	.remove = simatic_ipc_batt_elkhartlake_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt
> > platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> > b/drivers/platform/x86/simatic-ipc-batt-f7188x.c new file mode
> > 100644 index 000000000000..a0189b9289f6 --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt-f7188x.c
> > @@ -0,0 +1,70 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/x86/simatic-ipc-base.h>
> > +
> > +#include "simatic-ipc-batt.h"
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("INTC1020:01",  66, NULL, 2,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = {
> > +	.table = {
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0,
> > GPIO_ACTIVE_HIGH),
> > +		GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1,
> > GPIO_ACTIVE_HIGH),
> > +		{} /* Terminating entry */
> > +	},
> > +};
> > +
> > +static int simatic_ipc_batt_f7188x_remove(struct platform_device
> > *pdev) +{
> > +	const struct simatic_ipc_platform *plat =
> > pdev->dev.platform_data; +
> > +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> > +		return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_227g); +
> > +	return simatic_ipc_batt_remove(pdev,
> > &simatic_ipc_batt_gpio_table_bx_39a); +}
> > +
> > +static int simatic_ipc_batt_f7188x_probe(struct platform_device
> > *pdev) +{
> > +	const struct simatic_ipc_platform *plat =
> > pdev->dev.platform_data; +
> > +	if (plat->devmode == SIMATIC_IPC_DEVICE_227G)
> > +		return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_227g); +
> > +	return simatic_ipc_batt_probe(pdev,
> > &simatic_ipc_batt_gpio_table_bx_39a); +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_f7188x_probe,
> > +	.remove = simatic_ipc_batt_f7188x_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x
> > platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild
> > <henning.schild@siemens.com>"); diff --git
> > a/drivers/platform/x86/simatic-ipc-batt.c
> > b/drivers/platform/x86/simatic-ipc-batt.c new file mode 100644
> > index 000000000000..a83272475b9d --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt.c
> > @@ -0,0 +1,250 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Authors:
> > + *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/ioport.h>
> > +#include <linux/gpio/machine.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/jiffies.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/x86/simatic-ipc-base.h>
> > +#include <linux/sizes.h>
> > +
> > +#define BATT_DELAY_MS	(1000 * 60 * 60 * 24)	/* 24 h
> > delay */ +
> > +#define SIMATIC_IPC_BATT_LEVEL_FULL	3000
> > +#define SIMATIC_IPC_BATT_LEVEL_CRIT	2750
> > +#define SIMATIC_IPC_BATT_LEVEL_EMPTY	   0
> > +
> > +static struct simatic_ipc_batt {
> > +	u8 devmode;
> > +	long current_state;
> > +	struct gpio_desc *gpios[3];
> > +	unsigned long last_updated_jiffies;
> > +} priv;
> > +
> > +static long simatic_ipc_batt_read_gpio(void)
> > +{
> > +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> > +
> > +	if (priv.gpios[2]) {
> > +		gpiod_set_value(priv.gpios[2], 1);
> > +		msleep(150);
> > +	}
> > +
> > +	if (gpiod_get_value_cansleep(priv.gpios[0]))
> > +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> > +	else if (gpiod_get_value_cansleep(priv.gpios[1]))
> > +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +
> > +	if (priv.gpios[2])
> > +		gpiod_set_value(priv.gpios[2], 0);
> > +
> > +	return r;
> > +}
> > +
> > +#define SIMATIC_IPC_BATT_PORT_BASE	0x404D
> > +static struct resource simatic_ipc_batt_io_res =
> > +	DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1,
> > KBUILD_MODNAME); +
> > +static long simatic_ipc_batt_read_io(struct device *dev)
> > +{
> > +	long r = SIMATIC_IPC_BATT_LEVEL_FULL;
> > +	struct resource *res = &simatic_ipc_batt_io_res;
> > +	u8 val;
> > +
> > +	if (!request_muxed_region(res->start, resource_size(res),
> > res->name)) {
> > +		dev_err(dev, "Unable to register IO resource at
> > %pR\n", res);
> > +		return -EBUSY;
> > +	}
> > +
> > +	val = inb(SIMATIC_IPC_BATT_PORT_BASE);
> > +	release_region(simatic_ipc_batt_io_res.start,
> > resource_size(&simatic_ipc_batt_io_res)); +
> > +	if (val & (1 << 7))
> > +		r = SIMATIC_IPC_BATT_LEVEL_EMPTY;
> > +	else if (val & (1 << 6))
> > +		r = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +
> > +	return r;
> > +}
> > +
> > +static long simatic_ipc_batt_read_value(struct device *dev)
> > +{
> > +	unsigned long next_update;
> > +
> > +	next_update = priv.last_updated_jiffies +
> > msecs_to_jiffies(BATT_DELAY_MS);
> > +	if (time_after(jiffies, next_update) ||
> > !priv.last_updated_jiffies) {
> > +		switch (priv.devmode) {
> > +		case SIMATIC_IPC_DEVICE_127E:
> > +		case SIMATIC_IPC_DEVICE_227G:
> > +		case SIMATIC_IPC_DEVICE_BX_39A:
> > +			priv.current_state =
> > simatic_ipc_batt_read_gpio();
> > +			break;
> > +		case SIMATIC_IPC_DEVICE_227E:
> > +			priv.current_state =
> > simatic_ipc_batt_read_io(dev);
> > +			break;
> > +		}
> > +		priv.last_updated_jiffies = jiffies;
> > +		if (priv.current_state <
> > SIMATIC_IPC_BATT_LEVEL_FULL)
> > +			dev_warn(dev, "CMOS battery needs to be
> > replaced.");
> > +	}
> > +
> > +	return priv.current_state;
> > +}
> > +
> > +static int simatic_ipc_batt_read(struct device *dev, enum
> > hwmon_sensor_types type,
> > +				 u32 attr, int channel, long *val)
> > +{
> > +	switch (attr) {
> > +	case hwmon_in_input:
> > +		*val = simatic_ipc_batt_read_value(dev);
> > +		break;
> > +	case hwmon_in_lcrit:
> > +		*val = SIMATIC_IPC_BATT_LEVEL_CRIT;
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static umode_t simatic_ipc_batt_is_visible(const void *data, enum
> > hwmon_sensor_types type,
> > +					   u32 attr, int channel)
> > +{
> > +	if (attr == hwmon_in_input || attr == hwmon_in_lcrit)
> > +		return 0444;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct hwmon_ops simatic_ipc_batt_ops = {
> > +	.is_visible = simatic_ipc_batt_is_visible,
> > +	.read = simatic_ipc_batt_read,
> > +};
> > +
> > +static const struct hwmon_channel_info *simatic_ipc_batt_info[] = {
> > +	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT),
> > +	NULL
> > +};
> > +
> > +static const struct hwmon_chip_info simatic_ipc_batt_chip_info = {
> > +	.ops = &simatic_ipc_batt_ops,
> > +	.info = simatic_ipc_batt_info,
> > +};
> > +
> > +int simatic_ipc_batt_remove(struct platform_device *pdev, struct
> > gpiod_lookup_table *table) +{
> > +	gpiod_remove_lookup_table(table);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove);
> > +
> > +int simatic_ipc_batt_probe(struct platform_device *pdev, struct
> > gpiod_lookup_table *table) +{
> > +	struct simatic_ipc_platform *plat;
> > +	struct device *dev = &pdev->dev;
> > +	struct device *hwmon_dev;
> > +	int err;
> > +
> > +	plat = pdev->dev.platform_data;
> > +	priv.devmode = plat->devmode;
> > +
> > +	switch (priv.devmode) {
> > +	case SIMATIC_IPC_DEVICE_127E:
> > +	case SIMATIC_IPC_DEVICE_227G:
> > +	case SIMATIC_IPC_DEVICE_BX_39A:
> > +	case SIMATIC_IPC_DEVICE_BX_21A:
> > +		table->dev_id = dev_name(dev);
> > +		gpiod_add_lookup_table(table);
> > +		break;
> > +	case SIMATIC_IPC_DEVICE_227E:
> > +		goto nogpio;
> > +	default:
> > +		return -ENODEV;
> > +	}
> > +
> > +	priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery
> > empty", 0, GPIOD_IN);
> > +	if (IS_ERR(priv.gpios[0])) {
> > +		err = PTR_ERR(priv.gpios[0]);
> > +		priv.gpios[0] = NULL;
> > +		goto out;
> > +	}
> > +	priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery
> > low", 1, GPIOD_IN);
> > +	if (IS_ERR(priv.gpios[1])) {
> > +		err = PTR_ERR(priv.gpios[1]);
> > +		priv.gpios[1] = NULL;
> > +		goto out;
> > +	}
> > +
> > +	if (table->table[2].key) {
> > +		priv.gpios[2] = devm_gpiod_get_index(dev,
> > "CMOSBattery meter", 2, GPIOD_OUT_HIGH);
> > +		if (IS_ERR(priv.gpios[2])) {
> > +			err = PTR_ERR(priv.gpios[1]);
> > +			priv.gpios[2] = NULL;
> > +			goto out;
> > +		}
> > +	} else {
> > +		priv.gpios[2] = NULL;
> > +	}
> > +
> > +nogpio:
> > +	hwmon_dev = devm_hwmon_device_register_with_info(dev,
> > KBUILD_MODNAME,
> > +							 &priv,
> > +
> > &simatic_ipc_batt_chip_info,
> > +							 NULL);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto out;
> > +	}
> > +
> > +	/* warn about aging battery even if userspace never reads
> > hwmon */
> > +	simatic_ipc_batt_read_value(dev);
> > +
> > +	return 0;
> > +out:
> > +	simatic_ipc_batt_remove(pdev, table);
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe);
> > +
> > +static int simatic_ipc_batt_io_remove(struct platform_device *pdev)
> > +{
> > +	return simatic_ipc_batt_remove(pdev, NULL);
> > +}
> > +
> > +static int simatic_ipc_batt_io_probe(struct platform_device *pdev)
> > +{
> > +	return simatic_ipc_batt_probe(pdev, NULL);
> > +}
> > +
> > +static struct platform_driver simatic_ipc_batt_driver = {
> > +	.probe = simatic_ipc_batt_io_probe,
> > +	.remove = simatic_ipc_batt_io_remove,
> > +	.driver = {
> > +		.name = KBUILD_MODNAME,
> > +	},
> > +};
> > +
> > +module_platform_driver(simatic_ipc_batt_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:" KBUILD_MODNAME);
> > +MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
> > diff --git a/drivers/platform/x86/simatic-ipc-batt.h
> > b/drivers/platform/x86/simatic-ipc-batt.h new file mode 100644
> > index 000000000000..4545cd3e3026
> > --- /dev/null
> > +++ b/drivers/platform/x86/simatic-ipc-batt.h
> > @@ -0,0 +1,20 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Siemens SIMATIC IPC driver for CMOS battery monitoring
> > + *
> > + * Copyright (c) Siemens AG, 2023
> > + *
> > + * Author:
> > + *  Henning Schild <henning.schild@siemens.com>
> > + */
> > +
> > +#ifndef _SIMATIC_IPC_BATT_H
> > +#define _SIMATIC_IPC_BATT_H
> > +
> > +int simatic_ipc_batt_probe(struct platform_device *pdev,
> > +			   struct gpiod_lookup_table *table);
> > +
> > +int simatic_ipc_batt_remove(struct platform_device *pdev,
> > +			    struct gpiod_lookup_table *table);
> > +
> > +#endif /* _SIMATIC_IPC_BATT_H */
> > diff --git a/drivers/platform/x86/simatic-ipc.c
> > b/drivers/platform/x86/simatic-ipc.c index
> > 4402cd354104..6d2c94c17414 100644 ---
> > a/drivers/platform/x86/simatic-ipc.c +++
> > b/drivers/platform/x86/simatic-ipc.c @@ -21,6 +21,7 @@
> >  
> >  static struct platform_device *ipc_led_platform_device;
> >  static struct platform_device *ipc_wdt_platform_device;
> > +static struct platform_device *ipc_batt_platform_device;
> >  
> >  static const struct dmi_system_id simatic_ipc_whitelist[] = {
> >  	{
> > @@ -37,38 +38,71 @@ static struct {
> >  	u32 station_id;
> >  	u8 led_mode;
> >  	u8 wdt_mode;
> > +	u8 batt_mode;
> >  } device_modes[] = {
> > -	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_227E},
> > -	{SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_227E},
> > -	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > -	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_427E},
> > -	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_427E},
> > -	{SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_227G},
> > -	{SIMATIC_IPC_IPCBX_21A, SIMATIC_IPC_DEVICE_BX_21A,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC127E,
> > +		SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_127E},
> > +	{SIMATIC_IPC_IPC227D,
> > +		SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC227E,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E,
> > SIMATIC_IPC_DEVICE_227E},
> > +	{SIMATIC_IPC_IPC227G,
> > +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_227G},
> > +	{SIMATIC_IPC_IPC277E,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E,
> > SIMATIC_IPC_DEVICE_227E},
> > +	{SIMATIC_IPC_IPC427D,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC427E,
> > +		SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPC477E,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E,
> > SIMATIC_IPC_DEVICE_NONE},
> > +	{SIMATIC_IPC_IPCBX_39A,
> > +		SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_BX_39A},
> > +	{SIMATIC_IPC_IPCPX_39A,
> > +		SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G,
> > SIMATIC_IPC_DEVICE_BX_39A},
> > +	{SIMATIC_IPC_IPCBX_21A,
> > +		SIMATIC_IPC_DEVICE_BX_21A,
> > SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A}, };
> >  
> >  static int register_platform_devices(u32 station_id)
> >  {
> >  	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
> >  	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
> > -	char *pdevname = KBUILD_MODNAME "_leds";
> > +	u8 battmode = SIMATIC_IPC_DEVICE_NONE;
> > +	char *pdevname;
> >  	int i;
> >  
> > -	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
> > -
> >  	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
> >  		if (device_modes[i].station_id == station_id) {
> >  			ledmode = device_modes[i].led_mode;
> >  			wdtmode = device_modes[i].wdt_mode;
> > +			battmode = device_modes[i].batt_mode;
> >  			break;
> >  		}
> >  	}
> >  
> > +	if (battmode != SIMATIC_IPC_DEVICE_NONE) {
> > +		pdevname = KBUILD_MODNAME "_batt";
> > +		if (battmode == SIMATIC_IPC_DEVICE_127E)
> > +			pdevname = KBUILD_MODNAME
> > "_batt_apollolake";
> > +		if (battmode == SIMATIC_IPC_DEVICE_BX_21A)
> > +			pdevname = KBUILD_MODNAME
> > "_batt_elkhartlake";
> > +		if (battmode == SIMATIC_IPC_DEVICE_227G ||
> > battmode == SIMATIC_IPC_DEVICE_BX_39A)
> > +			pdevname = KBUILD_MODNAME "_batt_f7188x";
> > +		platform_data.devmode = battmode;
> > +		ipc_batt_platform_device =
> > +			platform_device_register_data(NULL,
> > pdevname,
> > +				PLATFORM_DEVID_NONE,
> > &platform_data,
> > +				sizeof(struct
> > simatic_ipc_platform));
> > +		if (IS_ERR(ipc_batt_platform_device))
> > +			return PTR_ERR(ipc_batt_platform_device);
> > +
> > +		pr_debug("device=%s created\n",
> > +			 ipc_batt_platform_device->name);
> > +	}
> > +
> >  	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
> > +		pdevname = KBUILD_MODNAME "_leds";
> >  		if (ledmode == SIMATIC_IPC_DEVICE_127E)
> >  			pdevname = KBUILD_MODNAME
> > "_leds_gpio_apollolake"; if (ledmode == SIMATIC_IPC_DEVICE_227G)
> > @@ -144,6 +178,9 @@ static void __exit simatic_ipc_exit_module(void)
> >  
> >  	platform_device_unregister(ipc_wdt_platform_device);
> >  	ipc_wdt_platform_device = NULL;
> > +
> > +	platform_device_unregister(ipc_batt_platform_device);
> > +	ipc_batt_platform_device = NULL;
> >  }
> >  
> >  module_init(simatic_ipc_init_module);
> > diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h
> > b/include/linux/platform_data/x86/simatic-ipc-base.h index
> > 00bf18ecb160..1ba47dd6939e 100644 ---
> > a/include/linux/platform_data/x86/simatic-ipc-base.h +++
> > b/include/linux/platform_data/x86/simatic-ipc-base.h @@ -21,6 +21,7
> > @@ #define SIMATIC_IPC_DEVICE_227E 4
> >  #define SIMATIC_IPC_DEVICE_227G 5
> >  #define SIMATIC_IPC_DEVICE_BX_21A 6
> > +#define SIMATIC_IPC_DEVICE_BX_39A 7
> >  
> >  struct simatic_ipc_platform {
> >  	u8	devmode;  
> 
> 


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

* Re: [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs
  2023-07-06 15:48 [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
  2023-07-06 15:48 ` [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed Henning Schild
  2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
@ 2023-07-14  9:28 ` Hans de Goede
  2 siblings, 0 replies; 14+ messages in thread
From: Hans de Goede @ 2023-07-14  9:28 UTC (permalink / raw)
  To: Henning Schild, Mark Gross, linux-kernel, platform-driver-x86
  Cc: Wim Van Sebroeck, Guenter Roeck, Jean Delvare, linux-watchdog,
	linux-hwmon, Tobias Schaffner, Gerd Haeussler

Hi,

On 7/6/23 17:48, Henning Schild wrote:
> The real change is in p2 and p1 just prepares to multiplex an IO region.
> We introduce a custom driver to read CMOS battery information on several
> of the Industrial PCs.
> 
> This is based on
>  "[PATCH 0/1] leds: simatic-ipc-leds-gpio: add new model BX-21A"
> 
> Henning Schild (2):
>   watchdog: simatic-ipc-wdt: make IO region access of one model muxed
>   platform/x86: add CMOS battery monitoring for simatic IPCs

Thank you. I've merged both into a platform-drivers-x86-simatic-ipc
branch where I'm collecting all the pending platform/x86: simatic-ipc
work.

I'll tag + send a pull-request to Lee Jones (for some dependent LED
patches) later today.

Guenter, I'll Cc you on the pull-req in case you also want to merge
the (immutable) tag to pick up the small watchdog change in this
series.

Regards,

Hans




> 
>  drivers/platform/x86/Kconfig                  |  48 ++++
>  drivers/platform/x86/Makefile                 |   6 +-
>  .../x86/simatic-ipc-batt-apollolake.c         |  51 ++++
>  .../x86/simatic-ipc-batt-elkhartlake.c        |  51 ++++
>  .../platform/x86/simatic-ipc-batt-f7188x.c    |  70 +++++
>  drivers/platform/x86/simatic-ipc-batt.c       | 250 ++++++++++++++++++
>  drivers/platform/x86/simatic-ipc-batt.h       |  20 ++
>  drivers/platform/x86/simatic-ipc.c            |  65 ++++-
>  drivers/watchdog/simatic-ipc-wdt.c            |   9 +-
>  .../platform_data/x86/simatic-ipc-base.h      |   1 +
>  10 files changed, 553 insertions(+), 18 deletions(-)
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-apollolake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-elkhartlake.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt-f7188x.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.c
>  create mode 100644 drivers/platform/x86/simatic-ipc-batt.h
> 


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

end of thread, other threads:[~2023-07-14  9:29 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-07-06 15:48 [PATCH 0/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
2023-07-06 15:48 ` [PATCH 1/2] watchdog: simatic-ipc-wdt: make IO region access of one model muxed Henning Schild
2023-07-06 16:03   ` Guenter Roeck
2023-07-10 13:33     ` Hans de Goede
2023-07-10 13:45       ` Guenter Roeck
2023-07-10 18:54         ` Henning Schild
2023-07-06 15:48 ` [PATCH 2/2] platform/x86: add CMOS battery monitoring for simatic IPCs Henning Schild
2023-07-10 13:34   ` Hans de Goede
2023-07-12 18:02   ` Hans de Goede
2023-07-12 18:05   ` Hans de Goede
2023-07-13  8:28     ` Henning Schild
2023-07-13  8:36       ` Hans de Goede
2023-07-13 11:59     ` Henning Schild
2023-07-14  9:28 ` [PATCH 0/2] " 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;
as well as URLs for NNTP newsgroup(s).