File Index
Files and directories
Sign in
FILE

kfan.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/kfan.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2025 KEBA Industrial Automation GmbH
  4 *
  5 * Driver for KEBA fan controller FPGA IP core
  6 *
  7 */
  8
  9#include <linux/hwmon.h>
 10#include <linux/io.h>
 11#include <linux/module.h>
 12#include <linux/device.h>
 13#include <linux/auxiliary_bus.h>
 14#include <linux/misc/keba.h>
 15
 16#define KFAN "kfan"
 17
 18#define KFAN_CONTROL_REG	0x04
 19
 20#define KFAN_STATUS_REG		0x08
 21#define   KFAN_STATUS_PRESENT	0x01
 22#define   KFAN_STATUS_REGULABLE	0x02
 23#define   KFAN_STATUS_TACHO	0x04
 24#define   KFAN_STATUS_BLOCKED	0x08
 25
 26#define KFAN_TACHO_REG		0x0c
 27
 28#define KFAN_DEFAULT_DIV	2
 29
 30struct kfan {
 31	void __iomem *base;
 32	bool tacho;
 33	bool regulable;
 34
 35	/* hwmon API configuration */
 36	u32 fan_channel_config[2];
 37	struct hwmon_channel_info fan_info;
 38	u32 pwm_channel_config[2];
 39	struct hwmon_channel_info pwm_info;
 40	const struct hwmon_channel_info *info[3];
 41	struct hwmon_chip_info chip;
 42};
 43
 44static bool kfan_get_fault(struct kfan *kfan)
 45{
 46	u8 status = ioread8(kfan->base + KFAN_STATUS_REG);
 47
 48	if (!(status & KFAN_STATUS_PRESENT))
 49		return true;
 50
 51	if (!kfan->tacho && (status & KFAN_STATUS_BLOCKED))
 52		return true;
 53
 54	return false;
 55}
 56
 57static unsigned int kfan_count_to_rpm(u16 count)
 58{
 59	if (count == 0 || count == 0xffff)
 60		return 0;
 61
 62	return 5000000UL / (KFAN_DEFAULT_DIV * count);
 63}
 64
 65static unsigned int kfan_get_rpm(struct kfan *kfan)
 66{
 67	unsigned int rpm;
 68	u16 count;
 69
 70	count = ioread16(kfan->base + KFAN_TACHO_REG);
 71	rpm = kfan_count_to_rpm(count);
 72
 73	return rpm;
 74}
 75
 76static unsigned int kfan_get_pwm(struct kfan *kfan)
 77{
 78	return ioread8(kfan->base + KFAN_CONTROL_REG);
 79}
 80
 81static int kfan_set_pwm(struct kfan *kfan, long val)
 82{
 83	if (val < 0 || val > 0xff)
 84		return -EINVAL;
 85
 86	/* if none-regulable, then only 0 or 0xff can be written */
 87	if (!kfan->regulable && val > 0)
 88		val = 0xff;
 89
 90	iowrite8(val, kfan->base + KFAN_CONTROL_REG);
 91
 92	return 0;
 93}
 94
 95static int kfan_write(struct device *dev, enum hwmon_sensor_types type,
 96		      u32 attr, int channel, long val)
 97{
 98	struct kfan *kfan = dev_get_drvdata(dev);
 99
100	switch (type) {
101	case hwmon_pwm:
102		switch (attr) {
103		case hwmon_pwm_input:
104			return kfan_set_pwm(kfan, val);
105		default:
106			break;
107		}
108		break;
109	default:
110		break;
111	}
112
113	return -EOPNOTSUPP;
114}
115
116static int kfan_read(struct device *dev, enum hwmon_sensor_types type,
117		     u32 attr, int channel, long *val)
118{
119	struct kfan *kfan = dev_get_drvdata(dev);
120
121	switch (type) {
122	case hwmon_fan:
123		switch (attr) {
124		case hwmon_fan_fault:
125			*val = kfan_get_fault(kfan);
126			return 0;
127		case hwmon_fan_input:
128			*val = kfan_get_rpm(kfan);
129			return 0;
130		default:
131			break;
132		}
133		break;
134	case hwmon_pwm:
135		switch (attr) {
136		case hwmon_pwm_input:
137			*val = kfan_get_pwm(kfan);
138			return 0;
139		default:
140			break;
141		}
142		break;
143	default:
144		break;
145	}
146
147	return -EOPNOTSUPP;
148}
149
150static umode_t kfan_is_visible(const void *data, enum hwmon_sensor_types type,
151			       u32 attr, int channel)
152{
153	switch (type) {
154	case hwmon_fan:
155		switch (attr) {
156		case hwmon_fan_input:
157			return 0444;
158		case hwmon_fan_fault:
159			return 0444;
160		default:
161			break;
162		}
163		break;
164	case hwmon_pwm:
165		switch (attr) {
166		case hwmon_pwm_input:
167			return 0644;
168		default:
169			break;
170		}
171		break;
172	default:
173		break;
174	}
175
176	return 0;
177}
178
179static const struct hwmon_ops kfan_hwmon_ops = {
180	.is_visible = kfan_is_visible,
181	.read = kfan_read,
182	.write = kfan_write,
183};
184
185static int kfan_probe(struct auxiliary_device *auxdev,
186		      const struct auxiliary_device_id *id)
187{
188	struct keba_fan_auxdev *kfan_auxdev =
189		container_of(auxdev, struct keba_fan_auxdev, auxdev);
190	struct device *dev = &auxdev->dev;
191	struct device *hwmon_dev;
192	struct kfan *kfan;
193	u8 status;
194
195	kfan = devm_kzalloc(dev, sizeof(*kfan), GFP_KERNEL);
196	if (!kfan)
197		return -ENOMEM;
198
199	kfan->base = devm_ioremap_resource(dev, &kfan_auxdev->io);
200	if (IS_ERR(kfan->base))
201		return PTR_ERR(kfan->base);
202
203	status = ioread8(kfan->base + KFAN_STATUS_REG);
204	if (status & KFAN_STATUS_REGULABLE)
205		kfan->regulable = true;
206	if (status & KFAN_STATUS_TACHO)
207		kfan->tacho = true;
208
209	/* fan */
210	kfan->fan_channel_config[0] = HWMON_F_FAULT;
211	if (kfan->tacho)
212		kfan->fan_channel_config[0] |= HWMON_F_INPUT;
213	kfan->fan_info.type = hwmon_fan;
214	kfan->fan_info.config = kfan->fan_channel_config;
215	kfan->info[0] = &kfan->fan_info;
216
217	/* PWM */
218	kfan->pwm_channel_config[0] = HWMON_PWM_INPUT;
219	kfan->pwm_info.type = hwmon_pwm;
220	kfan->pwm_info.config = kfan->pwm_channel_config;
221	kfan->info[1] = &kfan->pwm_info;
222
223	kfan->chip.ops = &kfan_hwmon_ops;
224	kfan->chip.info = kfan->info;
225	hwmon_dev = devm_hwmon_device_register_with_info(dev, KFAN, kfan,
226							 &kfan->chip, NULL);
227	return PTR_ERR_OR_ZERO(hwmon_dev);
228}
229
230static const struct auxiliary_device_id kfan_devtype_aux[] = {
231	{ .name = "keba.fan" },
232	{}
233};
234MODULE_DEVICE_TABLE(auxiliary, kfan_devtype_aux);
235
236static struct auxiliary_driver kfan_driver_aux = {
237	.name = KFAN,
238	.id_table = kfan_devtype_aux,
239	.probe = kfan_probe,
240};
241module_auxiliary_driver(kfan_driver_aux);
242
243MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
244MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
245MODULE_DESCRIPTION("KEBA fan controller driver");
246MODULE_LICENSE("GPL");
~/ Read-only file preview.