File Index
Files and directories
Sign in
FILE

kbatt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/kbatt.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2025 KEBA Industrial Automation GmbH
  4 *
  5 * Driver for KEBA battery monitoring controller FPGA IP core
  6 */
  7
  8#include <linux/hwmon.h>
  9#include <linux/io.h>
 10#include <linux/delay.h>
 11#include <linux/module.h>
 12#include <linux/device.h>
 13#include <linux/auxiliary_bus.h>
 14#include <linux/misc/keba.h>
 15#include <linux/mutex.h>
 16
 17#define KBATT "kbatt"
 18
 19#define KBATT_CONTROL_REG		0x4
 20#define   KBATT_CONTROL_BAT_TEST	0x01
 21
 22#define KBATT_STATUS_REG		0x8
 23#define   KBATT_STATUS_BAT_OK		0x01
 24
 25#define KBATT_MAX_UPD_INTERVAL	(10 * HZ)
 26#define KBATT_SETTLE_TIME_US	(100 * USEC_PER_MSEC)
 27
 28struct kbatt {
 29	/* update lock */
 30	struct mutex lock;
 31	void __iomem *base;
 32
 33	unsigned long next_update; /* in jiffies */
 34	bool alarm;
 35};
 36
 37static bool kbatt_alarm(struct kbatt *kbatt)
 38{
 39	mutex_lock(&kbatt->lock);
 40
 41	if (!kbatt->next_update || time_after(jiffies, kbatt->next_update)) {
 42		/* switch load on */
 43		iowrite8(KBATT_CONTROL_BAT_TEST,
 44			 kbatt->base + KBATT_CONTROL_REG);
 45
 46		/* wait some time to let things settle */
 47		fsleep(KBATT_SETTLE_TIME_US);
 48
 49		/* check battery state */
 50		if (ioread8(kbatt->base + KBATT_STATUS_REG) &
 51		    KBATT_STATUS_BAT_OK)
 52			kbatt->alarm = false;
 53		else
 54			kbatt->alarm = true;
 55
 56		/* switch load off */
 57		iowrite8(0, kbatt->base + KBATT_CONTROL_REG);
 58
 59		kbatt->next_update = jiffies + KBATT_MAX_UPD_INTERVAL;
 60	}
 61
 62	mutex_unlock(&kbatt->lock);
 63
 64	return kbatt->alarm;
 65}
 66
 67static int kbatt_read(struct device *dev, enum hwmon_sensor_types type,
 68		      u32 attr, int channel, long *val)
 69{
 70	struct kbatt *kbatt = dev_get_drvdata(dev);
 71
 72	*val = kbatt_alarm(kbatt) ? 1 : 0;
 73
 74	return 0;
 75}
 76
 77static umode_t kbatt_is_visible(const void *data, enum hwmon_sensor_types type,
 78				u32 attr, int channel)
 79{
 80	if (channel == 0 && attr == hwmon_in_min_alarm)
 81		return 0444;
 82
 83	return 0;
 84}
 85
 86static const struct hwmon_channel_info *kbatt_info[] = {
 87	HWMON_CHANNEL_INFO(in,
 88			   /* 0: input minimum alarm channel */
 89			   HWMON_I_MIN_ALARM),
 90	NULL
 91};
 92
 93static const struct hwmon_ops kbatt_hwmon_ops = {
 94	.is_visible = kbatt_is_visible,
 95	.read = kbatt_read,
 96};
 97
 98static const struct hwmon_chip_info kbatt_chip_info = {
 99	.ops = &kbatt_hwmon_ops,
100	.info = kbatt_info,
101};
102
103static int kbatt_probe(struct auxiliary_device *auxdev,
104		       const struct auxiliary_device_id *id)
105{
106	struct keba_batt_auxdev *kbatt_auxdev =
107		container_of(auxdev, struct keba_batt_auxdev, auxdev);
108	struct device *dev = &auxdev->dev;
109	struct device *hwmon_dev;
110	struct kbatt *kbatt;
111	int retval;
112
113	kbatt = devm_kzalloc(dev, sizeof(*kbatt), GFP_KERNEL);
114	if (!kbatt)
115		return -ENOMEM;
116
117	retval = devm_mutex_init(dev, &kbatt->lock);
118	if (retval)
119		return retval;
120
121	kbatt->base = devm_ioremap_resource(dev, &kbatt_auxdev->io);
122	if (IS_ERR(kbatt->base))
123		return PTR_ERR(kbatt->base);
124
125	hwmon_dev = devm_hwmon_device_register_with_info(dev, KBATT, kbatt,
126							 &kbatt_chip_info,
127							 NULL);
128	return PTR_ERR_OR_ZERO(hwmon_dev);
129}
130
131static const struct auxiliary_device_id kbatt_devtype_aux[] = {
132	{ .name = "keba.batt" },
133	{}
134};
135MODULE_DEVICE_TABLE(auxiliary, kbatt_devtype_aux);
136
137static struct auxiliary_driver kbatt_driver_aux = {
138	.name = KBATT,
139	.id_table = kbatt_devtype_aux,
140	.probe = kbatt_probe,
141};
142module_auxiliary_driver(kbatt_driver_aux);
143
144MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
145MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
146MODULE_DESCRIPTION("KEBA battery monitoring controller driver");
147MODULE_LICENSE("GPL");
~/ Read-only file preview.