#include #include #include #include #include #define FAN_DUTY 0x0 #define FAN_TACH 0x1 struct cds9k_fan { struct regmap *regmap; u32 base; }; static unsigned int cds9k_rpm_to_pwm(long rpm) { static long max_rpm = 5000; static unsigned int max_pwm = 255; rpm = clamp(rpm, (long)0, max_rpm); return max_pwm * rpm / max_rpm; } static umode_t cds9k_fan_is_visible(const void *fan, enum hwmon_sensor_types type, u32 attr, int channel) { if (channel != 0) return 0; if (type == hwmon_fan && attr == hwmon_fan_input) return 0444; if (type == hwmon_fan && attr == hwmon_fan_target) return 0644; return 0; } static int cds9k_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *value) { int ret; struct cds9k_fan *fan = dev_get_drvdata(dev); u32 reg_offs; unsigned int raw_value; if (channel != 0 || type != hwmon_fan) return -EINVAL; switch (attr) { case hwmon_fan_target: reg_offs = FAN_DUTY; break; case hwmon_fan_input: reg_offs = FAN_TACH; break; default: return -EINVAL; } ret = regmap_read(fan->regmap, fan->base + reg_offs, &raw_value); if (ret) return ret; *value = raw_value; return 0; } static int cds9k_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long value) { unsigned int target_pwm; struct cds9k_fan *fan = dev_get_drvdata(dev); if (channel != 0 || type != hwmon_fan || attr != hwmon_fan_target) return -EINVAL; target_pwm = cds9k_rpm_to_pwm(value); return regmap_write(fan->regmap, fan->base + FAN_DUTY, target_pwm); } static const struct hwmon_ops cds9k_fan_ops = { .is_visible = cds9k_fan_is_visible, .read = cds9k_fan_read, .write = cds9k_fan_write }; static const struct hwmon_channel_info *cds9k_fan_channel_info[] = { HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), HWMON_CHANNEL_INFO(fan, HWMON_F_TARGET), NULL }; static const struct hwmon_chip_info cds9k_fan_chip_info = { .ops = &cds9k_fan_ops, .info = cds9k_fan_channel_info }; static int cds9k_fan_probe(struct platform_device *pdev) { struct device *hwmon_dev; int ret; struct cds9k_fan *fan; const char *label; fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); if (!fan) return -ENOMEM; ret = device_property_read_u32(&pdev->dev, "reg", &fan->base); ret |= device_property_read_string(&pdev->dev, "label", &label); if (ret) return -EINVAL; if (!pdev->dev.parent) return -ENODEV; fan->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!fan->regmap) return -ENODEV; dev_info(&pdev->dev, "registering hwmon device\n"); hwmon_dev = devm_hwmon_device_register_with_info( &pdev->dev, label, fan, &cds9k_fan_chip_info, NULL ); return PTR_ERR_OR_ZERO(hwmon_dev); } static struct of_device_id cds9k_fan_of_match[] = { { .compatible = "david,cds9k-fan" }, {} }; MODULE_DEVICE_TABLE(of, cds9k_fan_of_match); static struct platform_driver cds9k_fan = { .driver = { .name = "cds9k-fan", .of_match_table = cds9k_fan_of_match, }, .probe = cds9k_fan_probe, }; module_platform_driver(cds9k_fan); MODULE_AUTHOR("David Phillips "); MODULE_DESCRIPTION("LED driver for the CDS9K board controller"); MODULE_LICENSE("GPL v2");