diff options
author | Gerhard Engleder <eg@keba.com> | 2025-04-09 21:08:30 +0200 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2025-04-23 07:18:27 -0700 |
commit | 7e581c193bde7d5ac49587d9a182e5d13e05547c (patch) | |
tree | d5f59f2415d978a550e346362b72df056dc9590b | |
parent | 0d01110e6356e95320091f36e3d7ce92fa597d1f (diff) |
hwmon: Add KEBA battery monitoring controller support
The KEBA battery monitoring controller is found in the system FPGA of
KEBA PLC devices. It puts a load on the coin cell battery to check the
state of the battery. If the coin cell battery is nearly empty, then
the user space is signaled with a hwmon alarm.
The auxiliary device for this driver is instantiated by the cp500 misc
driver.
Signed-off-by: Gerhard Engleder <eg@keba.com>
Link: https://lore.kernel.org/r/20250409190830.60489-1-gerhard@engleder-embedded.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r-- | Documentation/hwmon/index.rst | 1 | ||||
-rw-r--r-- | Documentation/hwmon/kbatt.rst | 60 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/kbatt.c | 147 |
5 files changed, 219 insertions, 0 deletions
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f0ddf6222c44..da5895115724 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -106,6 +106,7 @@ Hardware Monitoring Kernel Drivers jc42 k10temp k8temp + kbatt lan966x lineage-pem lm25066 diff --git a/Documentation/hwmon/kbatt.rst b/Documentation/hwmon/kbatt.rst new file mode 100644 index 000000000000..b72718c5ede3 --- /dev/null +++ b/Documentation/hwmon/kbatt.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver kbatt +=================== + +Supported chips: + + * KEBA battery monitoring controller (IP core in FPGA) + + Prefix: 'kbatt' + +Authors: + + Gerhard Engleder <eg@keba.com> + Petar Bojanic <boja@keba.com> + +Description +----------- + +The KEBA battery monitoring controller is an IP core for FPGAs, which +monitors the health of a coin cell battery. The coin cell battery is +typically used to supply the RTC during power off to keep the current +time. E.g., the CP500 FPGA includes this IP core to monitor the coin cell +battery of PLCs and the corresponding cp500 driver creates an auxiliary +device for the kbatt driver. + +This driver provides information about the coin cell battery health to +user space. Actually the user space shall be informed that the coin cell +battery is nearly empty and needs to be replaced. + +The coin cell battery must be tested actively to get to know if its nearly +empty or not. Therefore, a load is put on the coin cell battery and the +resulting voltage is evaluated. This evaluation is done by some hard wired +analog logic, which compares the voltage to a defined limit. If the +voltage is above the limit, then the coin cell battery is assumed to be +ok. If the voltage is below the limit, then the coin cell battery is +nearly empty (or broken, removed, ...) and shall be replaced by a new one. +The KEBA battery monitoring controller allows to start the test of the +coin cell battery and to get the result if the voltage is above or below +the limit. The actual voltage is not available. Only the information if +the voltage is below a limit is available. + +The test load, which is put on the coin cell battery for the health check, +is similar to the load during power off. Therefore, the lifetime of the +coin cell battery is reduced directly by the duration of each test. To +limit the negative impact to the lifetime the test is limited to at most +once every 10 seconds. The test load is put on the coin cell battery for +100ms. Thus, in worst case the coin cell battery lifetime is reduced by +1% of the uptime or 3.65 days per year. As the coin cell battery lasts +multiple years, this lifetime reduction negligible. + +This driver only provides a single alarm attribute, which is raised when +the coin cell battery is nearly empty. + +====================== ==== =================================================== +Attribute R/W Contents +====================== ==== =================================================== +in0_min_alarm R voltage of coin cell battery under load is below + limit +====================== ==== =================================================== diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f91f713b0105..832d7e5f9f7b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -335,6 +335,16 @@ config SENSORS_K10TEMP This driver can also be built as a module. If so, the module will be called k10temp. +config SENSORS_KBATT + tristate "KEBA battery controller support" + depends on KEBA_CP500 + help + This driver supports the battery monitoring controller found in + KEBA system FPGA devices. + + This driver can also be built as a module. If so, the module + will be called kbatt. + config SENSORS_FAM15H_POWER tristate "AMD Family 15h processor power" depends on X86 && PCI && CPU_SUP_AMD diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 766c652ef22b..af18deb0422e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o +obj-$(CONFIG_SENSORS_KBATT) += kbatt.o obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o obj-$(CONFIG_SENSORS_LENOVO_EC) += lenovo-ec-sensors.o obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o diff --git a/drivers/hwmon/kbatt.c b/drivers/hwmon/kbatt.c new file mode 100644 index 000000000000..501b8f4ded33 --- /dev/null +++ b/drivers/hwmon/kbatt.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 KEBA Industrial Automation GmbH + * + * Driver for KEBA battery monitoring controller FPGA IP core + */ + +#include <linux/hwmon.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/auxiliary_bus.h> +#include <linux/misc/keba.h> +#include <linux/mutex.h> + +#define KBATT "kbatt" + +#define KBATT_CONTROL_REG 0x4 +#define KBATT_CONTROL_BAT_TEST 0x01 + +#define KBATT_STATUS_REG 0x8 +#define KBATT_STATUS_BAT_OK 0x01 + +#define KBATT_MAX_UPD_INTERVAL (10 * HZ) +#define KBATT_SETTLE_TIME_US (100 * USEC_PER_MSEC) + +struct kbatt { + /* update lock */ + struct mutex lock; + void __iomem *base; + + unsigned long next_update; /* in jiffies */ + bool alarm; +}; + +static bool kbatt_alarm(struct kbatt *kbatt) +{ + mutex_lock(&kbatt->lock); + + if (!kbatt->next_update || time_after(jiffies, kbatt->next_update)) { + /* switch load on */ + iowrite8(KBATT_CONTROL_BAT_TEST, + kbatt->base + KBATT_CONTROL_REG); + + /* wait some time to let things settle */ + fsleep(KBATT_SETTLE_TIME_US); + + /* check battery state */ + if (ioread8(kbatt->base + KBATT_STATUS_REG) & + KBATT_STATUS_BAT_OK) + kbatt->alarm = false; + else + kbatt->alarm = true; + + /* switch load off */ + iowrite8(0, kbatt->base + KBATT_CONTROL_REG); + + kbatt->next_update = jiffies + KBATT_MAX_UPD_INTERVAL; + } + + mutex_unlock(&kbatt->lock); + + return kbatt->alarm; +} + +static int kbatt_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct kbatt *kbatt = dev_get_drvdata(dev); + + *val = kbatt_alarm(kbatt) ? 1 : 0; + + return 0; +} + +static umode_t kbatt_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (channel == 0 && attr == hwmon_in_min_alarm) + return 0444; + + return 0; +} + +static const struct hwmon_channel_info *kbatt_info[] = { + HWMON_CHANNEL_INFO(in, + /* 0: input minimum alarm channel */ + HWMON_I_MIN_ALARM), + NULL +}; + +static const struct hwmon_ops kbatt_hwmon_ops = { + .is_visible = kbatt_is_visible, + .read = kbatt_read, +}; + +static const struct hwmon_chip_info kbatt_chip_info = { + .ops = &kbatt_hwmon_ops, + .info = kbatt_info, +}; + +static int kbatt_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct keba_batt_auxdev *kbatt_auxdev = + container_of(auxdev, struct keba_batt_auxdev, auxdev); + struct device *dev = &auxdev->dev; + struct device *hwmon_dev; + struct kbatt *kbatt; + int retval; + + kbatt = devm_kzalloc(dev, sizeof(*kbatt), GFP_KERNEL); + if (!kbatt) + return -ENOMEM; + + retval = devm_mutex_init(dev, &kbatt->lock); + if (retval) + return retval; + + kbatt->base = devm_ioremap_resource(dev, &kbatt_auxdev->io); + if (IS_ERR(kbatt->base)) + return PTR_ERR(kbatt->base); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, KBATT, kbatt, + &kbatt_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct auxiliary_device_id kbatt_devtype_aux[] = { + { .name = "keba.batt" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, kbatt_devtype_aux); + +static struct auxiliary_driver kbatt_driver_aux = { + .name = KBATT, + .id_table = kbatt_devtype_aux, + .probe = kbatt_probe, +}; +module_auxiliary_driver(kbatt_driver_aux); + +MODULE_AUTHOR("Petar Bojanic <boja@keba.com>"); +MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); +MODULE_DESCRIPTION("KEBA battery monitoring controller driver"); +MODULE_LICENSE("GPL"); |