| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2025 KEBA Industrial Automation GmbH
- *
- * Driver for KEBA fan controller FPGA IP core
- *
- */
- #include <linux/hwmon.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/auxiliary_bus.h>
- #include <linux/misc/keba.h>
- #define KFAN "kfan"
- #define KFAN_CONTROL_REG 0x04
- #define KFAN_STATUS_REG 0x08
- #define KFAN_STATUS_PRESENT 0x01
- #define KFAN_STATUS_REGULABLE 0x02
- #define KFAN_STATUS_TACHO 0x04
- #define KFAN_STATUS_BLOCKED 0x08
- #define KFAN_TACHO_REG 0x0c
- #define KFAN_DEFAULT_DIV 2
- struct kfan {
- void __iomem *base;
- bool tacho;
- bool regulable;
- /* hwmon API configuration */
- u32 fan_channel_config[2];
- struct hwmon_channel_info fan_info;
- u32 pwm_channel_config[2];
- struct hwmon_channel_info pwm_info;
- const struct hwmon_channel_info *info[3];
- struct hwmon_chip_info chip;
- };
- static bool kfan_get_fault(struct kfan *kfan)
- {
- u8 status = ioread8(kfan->base + KFAN_STATUS_REG);
- if (!(status & KFAN_STATUS_PRESENT))
- return true;
- if (!kfan->tacho && (status & KFAN_STATUS_BLOCKED))
- return true;
- return false;
- }
- static unsigned int kfan_count_to_rpm(u16 count)
- {
- if (count == 0 || count == 0xffff)
- return 0;
- return 5000000UL / (KFAN_DEFAULT_DIV * count);
- }
- static unsigned int kfan_get_rpm(struct kfan *kfan)
- {
- unsigned int rpm;
- u16 count;
- count = ioread16(kfan->base + KFAN_TACHO_REG);
- rpm = kfan_count_to_rpm(count);
- return rpm;
- }
- static unsigned int kfan_get_pwm(struct kfan *kfan)
- {
- return ioread8(kfan->base + KFAN_CONTROL_REG);
- }
- static int kfan_set_pwm(struct kfan *kfan, long val)
- {
- if (val < 0 || val > 0xff)
- return -EINVAL;
- /* if none-regulable, then only 0 or 0xff can be written */
- if (!kfan->regulable && val > 0)
- val = 0xff;
- iowrite8(val, kfan->base + KFAN_CONTROL_REG);
- return 0;
- }
- static int kfan_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
- {
- struct kfan *kfan = dev_get_drvdata(dev);
- switch (type) {
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- return kfan_set_pwm(kfan, val);
- default:
- break;
- }
- break;
- default:
- break;
- }
- return -EOPNOTSUPP;
- }
- static int kfan_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
- {
- struct kfan *kfan = dev_get_drvdata(dev);
- switch (type) {
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_fault:
- *val = kfan_get_fault(kfan);
- return 0;
- case hwmon_fan_input:
- *val = kfan_get_rpm(kfan);
- return 0;
- default:
- break;
- }
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- *val = kfan_get_pwm(kfan);
- return 0;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return -EOPNOTSUPP;
- }
- static umode_t kfan_is_visible(const void *data, enum hwmon_sensor_types type,
- u32 attr, int channel)
- {
- switch (type) {
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_input:
- return 0444;
- case hwmon_fan_fault:
- return 0444;
- default:
- break;
- }
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- return 0644;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return 0;
- }
- static const struct hwmon_ops kfan_hwmon_ops = {
- .is_visible = kfan_is_visible,
- .read = kfan_read,
- .write = kfan_write,
- };
- static int kfan_probe(struct auxiliary_device *auxdev,
- const struct auxiliary_device_id *id)
- {
- struct keba_fan_auxdev *kfan_auxdev =
- container_of(auxdev, struct keba_fan_auxdev, auxdev);
- struct device *dev = &auxdev->dev;
- struct device *hwmon_dev;
- struct kfan *kfan;
- u8 status;
- kfan = devm_kzalloc(dev, sizeof(*kfan), GFP_KERNEL);
- if (!kfan)
- return -ENOMEM;
- kfan->base = devm_ioremap_resource(dev, &kfan_auxdev->io);
- if (IS_ERR(kfan->base))
- return PTR_ERR(kfan->base);
- status = ioread8(kfan->base + KFAN_STATUS_REG);
- if (status & KFAN_STATUS_REGULABLE)
- kfan->regulable = true;
- if (status & KFAN_STATUS_TACHO)
- kfan->tacho = true;
- /* fan */
- kfan->fan_channel_config[0] = HWMON_F_FAULT;
- if (kfan->tacho)
- kfan->fan_channel_config[0] |= HWMON_F_INPUT;
- kfan->fan_info.type = hwmon_fan;
- kfan->fan_info.config = kfan->fan_channel_config;
- kfan->info[0] = &kfan->fan_info;
- /* PWM */
- kfan->pwm_channel_config[0] = HWMON_PWM_INPUT;
- kfan->pwm_info.type = hwmon_pwm;
- kfan->pwm_info.config = kfan->pwm_channel_config;
- kfan->info[1] = &kfan->pwm_info;
- kfan->chip.ops = &kfan_hwmon_ops;
- kfan->chip.info = kfan->info;
- hwmon_dev = devm_hwmon_device_register_with_info(dev, KFAN, kfan,
- &kfan->chip, NULL);
- return PTR_ERR_OR_ZERO(hwmon_dev);
- }
- static const struct auxiliary_device_id kfan_devtype_aux[] = {
- { .name = "keba.fan" },
- {}
- };
- MODULE_DEVICE_TABLE(auxiliary, kfan_devtype_aux);
- static struct auxiliary_driver kfan_driver_aux = {
- .name = KFAN,
- .id_table = kfan_devtype_aux,
- .probe = kfan_probe,
- };
- module_auxiliary_driver(kfan_driver_aux);
- MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
- MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
- MODULE_DESCRIPTION("KEBA fan controller driver");
- MODULE_LICENSE("GPL");
|