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
 
 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
 
--- /dev/null
+// 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"
+
+static 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");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
 
--- /dev/null
+// 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"
+
+static 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");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
 
--- /dev/null
+// 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"
+
+static 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 */
+       },
+};
+
+static 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");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
 
--- /dev/null
+// 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>
+
+#include "simatic-ipc-batt.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");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
 
--- /dev/null
+/* 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 */
 
 
 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[] = {
        {
        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)
 
        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);
 
 #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;