Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: add generic GPIO fan driver

This patch adds hwmon support for fans connected to GPIO lines.

Platform specific information such as GPIO pinout and speed conversion array
(rpm from/to GPIO value) are passed to the driver via platform_data.

Signed-off-by: Simon Guinot <sguinot@lacie.com>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>

authored by

Simon Guinot and committed by
Guenter Roeck
d6fe1360 32478006

+604
+9
drivers/hwmon/Kconfig
··· 399 399 This driver can also be built as a module. If so, the module 400 400 will be called gl520sm. 401 401 402 + config SENSORS_GPIO_FAN 403 + tristate "GPIO fan" 404 + depends on GENERIC_GPIO 405 + help 406 + If you say yes here you get support for fans connected to GPIO lines. 407 + 408 + This driver can also be built as a module. If so, the module 409 + will be called gpio-fan. 410 + 402 411 config SENSORS_CORETEMP 403 412 tristate "Intel Core/Core2/Atom temperature sensor" 404 413 depends on X86 && PCI && EXPERIMENTAL
+1
drivers/hwmon/Makefile
··· 51 51 obj-$(CONFIG_SENSORS_G760A) += g760a.o 52 52 obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o 53 53 obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o 54 + obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o 54 55 obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o 55 56 obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o 56 57 obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
+558
drivers/hwmon/gpio-fan.c
··· 1 + /* 2 + * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. 3 + * 4 + * Copyright (C) 2010 LaCie 5 + * 6 + * Author: Simon Guinot <sguinot@lacie.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + * 13 + * This program is distributed in the hope that it will be useful, 14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + * GNU General Public License for more details. 17 + * 18 + * You should have received a copy of the GNU General Public License 19 + * along with this program; if not, write to the Free Software 20 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 + */ 22 + 23 + #include <linux/module.h> 24 + #include <linux/init.h> 25 + #include <linux/slab.h> 26 + #include <linux/interrupt.h> 27 + #include <linux/irq.h> 28 + #include <linux/platform_device.h> 29 + #include <linux/err.h> 30 + #include <linux/mutex.h> 31 + #include <linux/hwmon.h> 32 + #include <linux/gpio.h> 33 + #include <linux/gpio-fan.h> 34 + 35 + struct gpio_fan_data { 36 + struct platform_device *pdev; 37 + struct device *hwmon_dev; 38 + struct mutex lock; /* lock GPIOs operations. */ 39 + int num_ctrl; 40 + unsigned *ctrl; 41 + int num_speed; 42 + struct gpio_fan_speed *speed; 43 + int speed_index; 44 + #ifdef CONFIG_PM 45 + int resume_speed; 46 + #endif 47 + bool pwm_enable; 48 + struct gpio_fan_alarm *alarm; 49 + struct work_struct alarm_work; 50 + }; 51 + 52 + /* 53 + * Alarm GPIO. 54 + */ 55 + 56 + static void fan_alarm_notify(struct work_struct *ws) 57 + { 58 + struct gpio_fan_data *fan_data = 59 + container_of(ws, struct gpio_fan_data, alarm_work); 60 + 61 + sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); 62 + kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); 63 + } 64 + 65 + static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) 66 + { 67 + struct gpio_fan_data *fan_data = dev_id; 68 + 69 + schedule_work(&fan_data->alarm_work); 70 + 71 + return IRQ_NONE; 72 + } 73 + 74 + static ssize_t show_fan_alarm(struct device *dev, 75 + struct device_attribute *attr, char *buf) 76 + { 77 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 78 + struct gpio_fan_alarm *alarm = fan_data->alarm; 79 + int value = gpio_get_value(alarm->gpio); 80 + 81 + if (alarm->active_low) 82 + value = !value; 83 + 84 + return sprintf(buf, "%d\n", value); 85 + } 86 + 87 + static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); 88 + 89 + static int fan_alarm_init(struct gpio_fan_data *fan_data, 90 + struct gpio_fan_alarm *alarm) 91 + { 92 + int err; 93 + int alarm_irq; 94 + struct platform_device *pdev = fan_data->pdev; 95 + 96 + fan_data->alarm = alarm; 97 + 98 + err = gpio_request(alarm->gpio, "GPIO fan alarm"); 99 + if (err) 100 + return err; 101 + 102 + err = gpio_direction_input(alarm->gpio); 103 + if (err) 104 + goto err_free_gpio; 105 + 106 + err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); 107 + if (err) 108 + goto err_free_gpio; 109 + 110 + /* 111 + * If the alarm GPIO don't support interrupts, just leave 112 + * without initializing the fail notification support. 113 + */ 114 + alarm_irq = gpio_to_irq(alarm->gpio); 115 + if (alarm_irq < 0) 116 + return 0; 117 + 118 + INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); 119 + set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); 120 + err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, 121 + "GPIO fan alarm", fan_data); 122 + if (err) 123 + goto err_free_sysfs; 124 + 125 + return 0; 126 + 127 + err_free_sysfs: 128 + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); 129 + err_free_gpio: 130 + gpio_free(alarm->gpio); 131 + 132 + return err; 133 + } 134 + 135 + static void fan_alarm_free(struct gpio_fan_data *fan_data) 136 + { 137 + struct platform_device *pdev = fan_data->pdev; 138 + int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); 139 + 140 + if (alarm_irq >= 0) 141 + free_irq(alarm_irq, fan_data); 142 + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); 143 + gpio_free(fan_data->alarm->gpio); 144 + } 145 + 146 + /* 147 + * Control GPIOs. 148 + */ 149 + 150 + /* Must be called with fan_data->lock held, except during initialization. */ 151 + static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) 152 + { 153 + int i; 154 + 155 + for (i = 0; i < fan_data->num_ctrl; i++) 156 + gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); 157 + } 158 + 159 + static int __get_fan_ctrl(struct gpio_fan_data *fan_data) 160 + { 161 + int i; 162 + int ctrl_val = 0; 163 + 164 + for (i = 0; i < fan_data->num_ctrl; i++) { 165 + int value; 166 + 167 + value = gpio_get_value(fan_data->ctrl[i]); 168 + ctrl_val |= (value << i); 169 + } 170 + return ctrl_val; 171 + } 172 + 173 + /* Must be called with fan_data->lock held, except during initialization. */ 174 + static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) 175 + { 176 + if (fan_data->speed_index == speed_index) 177 + return; 178 + 179 + __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); 180 + fan_data->speed_index = speed_index; 181 + } 182 + 183 + static int get_fan_speed_index(struct gpio_fan_data *fan_data) 184 + { 185 + int ctrl_val = __get_fan_ctrl(fan_data); 186 + int i; 187 + 188 + for (i = 0; i < fan_data->num_speed; i++) 189 + if (fan_data->speed[i].ctrl_val == ctrl_val) 190 + return i; 191 + 192 + dev_warn(&fan_data->pdev->dev, 193 + "missing speed array entry for GPIO value 0x%x\n", ctrl_val); 194 + 195 + return -EINVAL; 196 + } 197 + 198 + static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) 199 + { 200 + struct gpio_fan_speed *speed = fan_data->speed; 201 + int i; 202 + 203 + for (i = 0; i < fan_data->num_speed; i++) 204 + if (speed[i].rpm >= rpm) 205 + return i; 206 + 207 + return fan_data->num_speed - 1; 208 + } 209 + 210 + static ssize_t show_pwm(struct device *dev, 211 + struct device_attribute *attr, char *buf) 212 + { 213 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 214 + u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); 215 + 216 + return sprintf(buf, "%d\n", pwm); 217 + } 218 + 219 + static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, 220 + const char *buf, size_t count) 221 + { 222 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 223 + unsigned long pwm; 224 + int speed_index; 225 + int ret = count; 226 + 227 + if (strict_strtoul(buf, 10, &pwm) || pwm > 255) 228 + return -EINVAL; 229 + 230 + mutex_lock(&fan_data->lock); 231 + 232 + if (!fan_data->pwm_enable) { 233 + ret = -EPERM; 234 + goto exit_unlock; 235 + } 236 + 237 + speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); 238 + set_fan_speed(fan_data, speed_index); 239 + 240 + exit_unlock: 241 + mutex_unlock(&fan_data->lock); 242 + 243 + return ret; 244 + } 245 + 246 + static ssize_t show_pwm_enable(struct device *dev, 247 + struct device_attribute *attr, char *buf) 248 + { 249 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 250 + 251 + return sprintf(buf, "%d\n", fan_data->pwm_enable); 252 + } 253 + 254 + static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, 255 + const char *buf, size_t count) 256 + { 257 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 258 + unsigned long val; 259 + 260 + if (strict_strtoul(buf, 10, &val) || val > 1) 261 + return -EINVAL; 262 + 263 + if (fan_data->pwm_enable == val) 264 + return count; 265 + 266 + mutex_lock(&fan_data->lock); 267 + 268 + fan_data->pwm_enable = val; 269 + 270 + /* Disable manual control mode: set fan at full speed. */ 271 + if (val == 0) 272 + set_fan_speed(fan_data, fan_data->num_speed - 1); 273 + 274 + mutex_unlock(&fan_data->lock); 275 + 276 + return count; 277 + } 278 + 279 + static ssize_t show_pwm_mode(struct device *dev, 280 + struct device_attribute *attr, char *buf) 281 + { 282 + return sprintf(buf, "0\n"); 283 + } 284 + 285 + static ssize_t show_rpm_min(struct device *dev, 286 + struct device_attribute *attr, char *buf) 287 + { 288 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 289 + 290 + return sprintf(buf, "%d\n", fan_data->speed[0].rpm); 291 + } 292 + 293 + static ssize_t show_rpm_max(struct device *dev, 294 + struct device_attribute *attr, char *buf) 295 + { 296 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 297 + 298 + return sprintf(buf, "%d\n", 299 + fan_data->speed[fan_data->num_speed - 1].rpm); 300 + } 301 + 302 + static ssize_t show_rpm(struct device *dev, 303 + struct device_attribute *attr, char *buf) 304 + { 305 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 306 + 307 + return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); 308 + } 309 + 310 + static ssize_t set_rpm(struct device *dev, struct device_attribute *attr, 311 + const char *buf, size_t count) 312 + { 313 + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); 314 + unsigned long rpm; 315 + int ret = count; 316 + 317 + if (strict_strtoul(buf, 10, &rpm)) 318 + return -EINVAL; 319 + 320 + mutex_lock(&fan_data->lock); 321 + 322 + if (!fan_data->pwm_enable) { 323 + ret = -EPERM; 324 + goto exit_unlock; 325 + } 326 + 327 + set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); 328 + 329 + exit_unlock: 330 + mutex_unlock(&fan_data->lock); 331 + 332 + return ret; 333 + } 334 + 335 + static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); 336 + static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, 337 + show_pwm_enable, set_pwm_enable); 338 + static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL); 339 + static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL); 340 + static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); 341 + static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); 342 + static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); 343 + 344 + static struct attribute *gpio_fan_ctrl_attributes[] = { 345 + &dev_attr_pwm1.attr, 346 + &dev_attr_pwm1_enable.attr, 347 + &dev_attr_pwm1_mode.attr, 348 + &dev_attr_fan1_input.attr, 349 + &dev_attr_fan1_target.attr, 350 + &dev_attr_fan1_min.attr, 351 + &dev_attr_fan1_max.attr, 352 + NULL 353 + }; 354 + 355 + static const struct attribute_group gpio_fan_ctrl_group = { 356 + .attrs = gpio_fan_ctrl_attributes, 357 + }; 358 + 359 + static int fan_ctrl_init(struct gpio_fan_data *fan_data, 360 + struct gpio_fan_platform_data *pdata) 361 + { 362 + struct platform_device *pdev = fan_data->pdev; 363 + int num_ctrl = pdata->num_ctrl; 364 + unsigned *ctrl = pdata->ctrl; 365 + int i, err; 366 + 367 + for (i = 0; i < num_ctrl; i++) { 368 + err = gpio_request(ctrl[i], "GPIO fan control"); 369 + if (err) 370 + goto err_free_gpio; 371 + 372 + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); 373 + if (err) { 374 + gpio_free(ctrl[i]); 375 + goto err_free_gpio; 376 + } 377 + } 378 + 379 + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); 380 + if (err) 381 + goto err_free_gpio; 382 + 383 + fan_data->num_ctrl = num_ctrl; 384 + fan_data->ctrl = ctrl; 385 + fan_data->num_speed = pdata->num_speed; 386 + fan_data->speed = pdata->speed; 387 + fan_data->pwm_enable = true; /* Enable manual fan speed control. */ 388 + fan_data->speed_index = get_fan_speed_index(fan_data); 389 + if (fan_data->speed_index < 0) { 390 + err = -ENODEV; 391 + goto err_free_gpio; 392 + } 393 + 394 + return 0; 395 + 396 + err_free_gpio: 397 + for (i = i - 1; i >= 0; i--) 398 + gpio_free(ctrl[i]); 399 + 400 + return err; 401 + } 402 + 403 + static void fan_ctrl_free(struct gpio_fan_data *fan_data) 404 + { 405 + struct platform_device *pdev = fan_data->pdev; 406 + int i; 407 + 408 + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); 409 + for (i = 0; i < fan_data->num_ctrl; i++) 410 + gpio_free(fan_data->ctrl[i]); 411 + } 412 + 413 + /* 414 + * Platform driver. 415 + */ 416 + 417 + static ssize_t show_name(struct device *dev, 418 + struct device_attribute *attr, char *buf) 419 + { 420 + return sprintf(buf, "gpio-fan\n"); 421 + } 422 + 423 + static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 424 + 425 + static int __devinit gpio_fan_probe(struct platform_device *pdev) 426 + { 427 + int err; 428 + struct gpio_fan_data *fan_data; 429 + struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; 430 + 431 + if (!pdata) 432 + return -EINVAL; 433 + 434 + fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); 435 + if (!fan_data) 436 + return -ENOMEM; 437 + 438 + fan_data->pdev = pdev; 439 + platform_set_drvdata(pdev, fan_data); 440 + mutex_init(&fan_data->lock); 441 + 442 + /* Configure alarm GPIO if available. */ 443 + if (pdata->alarm) { 444 + err = fan_alarm_init(fan_data, pdata->alarm); 445 + if (err) 446 + goto err_free_data; 447 + } 448 + 449 + /* Configure control GPIOs if available. */ 450 + if (pdata->ctrl && pdata->num_ctrl > 0) { 451 + if (!pdata->speed || pdata->num_speed <= 1) { 452 + err = -EINVAL; 453 + goto err_free_alarm; 454 + } 455 + err = fan_ctrl_init(fan_data, pdata); 456 + if (err) 457 + goto err_free_alarm; 458 + } 459 + 460 + err = device_create_file(&pdev->dev, &dev_attr_name); 461 + if (err) 462 + goto err_free_ctrl; 463 + 464 + /* Make this driver part of hwmon class. */ 465 + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); 466 + if (IS_ERR(fan_data->hwmon_dev)) { 467 + err = PTR_ERR(fan_data->hwmon_dev); 468 + goto err_remove_name; 469 + } 470 + 471 + dev_info(&pdev->dev, "GPIO fan initialized\n"); 472 + 473 + return 0; 474 + 475 + err_remove_name: 476 + device_remove_file(&pdev->dev, &dev_attr_name); 477 + err_free_ctrl: 478 + if (fan_data->ctrl) 479 + fan_ctrl_free(fan_data); 480 + err_free_alarm: 481 + if (fan_data->alarm) 482 + fan_alarm_free(fan_data); 483 + err_free_data: 484 + platform_set_drvdata(pdev, NULL); 485 + kfree(fan_data); 486 + 487 + return err; 488 + } 489 + 490 + static int __devexit gpio_fan_remove(struct platform_device *pdev) 491 + { 492 + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); 493 + 494 + hwmon_device_unregister(fan_data->hwmon_dev); 495 + device_remove_file(&pdev->dev, &dev_attr_name); 496 + if (fan_data->alarm) 497 + fan_alarm_free(fan_data); 498 + if (fan_data->ctrl) 499 + fan_ctrl_free(fan_data); 500 + kfree(fan_data); 501 + 502 + return 0; 503 + } 504 + 505 + #ifdef CONFIG_PM 506 + static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) 507 + { 508 + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); 509 + 510 + if (fan_data->ctrl) { 511 + fan_data->resume_speed = fan_data->speed_index; 512 + set_fan_speed(fan_data, 0); 513 + } 514 + 515 + return 0; 516 + } 517 + 518 + static int gpio_fan_resume(struct platform_device *pdev) 519 + { 520 + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); 521 + 522 + if (fan_data->ctrl) 523 + set_fan_speed(fan_data, fan_data->resume_speed); 524 + 525 + return 0; 526 + } 527 + #else 528 + #define gpio_fan_suspend NULL 529 + #define gpio_fan_resume NULL 530 + #endif 531 + 532 + static struct platform_driver gpio_fan_driver = { 533 + .probe = gpio_fan_probe, 534 + .remove = __devexit_p(gpio_fan_remove), 535 + .suspend = gpio_fan_suspend, 536 + .resume = gpio_fan_resume, 537 + .driver = { 538 + .name = "gpio-fan", 539 + }, 540 + }; 541 + 542 + static int __init gpio_fan_init(void) 543 + { 544 + return platform_driver_register(&gpio_fan_driver); 545 + } 546 + 547 + static void __exit gpio_fan_exit(void) 548 + { 549 + platform_driver_unregister(&gpio_fan_driver); 550 + } 551 + 552 + module_init(gpio_fan_init); 553 + module_exit(gpio_fan_exit); 554 + 555 + MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 556 + MODULE_DESCRIPTION("GPIO FAN driver"); 557 + MODULE_LICENSE("GPL"); 558 + MODULE_ALIAS("platform:gpio-fan");
+36
include/linux/gpio-fan.h
··· 1 + /* 2 + * include/linux/gpio-fan.h 3 + * 4 + * Platform data structure for GPIO fan driver 5 + * 6 + * This file is licensed under the terms of the GNU General Public 7 + * License version 2. This program is licensed "as is" without any 8 + * warranty of any kind, whether express or implied. 9 + */ 10 + 11 + #ifndef __LINUX_GPIO_FAN_H 12 + #define __LINUX_GPIO_FAN_H 13 + 14 + struct gpio_fan_alarm { 15 + unsigned gpio; 16 + unsigned active_low; 17 + }; 18 + 19 + struct gpio_fan_speed { 20 + int rpm; 21 + int ctrl_val; 22 + }; 23 + 24 + struct gpio_fan_platform_data { 25 + int num_ctrl; 26 + unsigned *ctrl; /* fan control GPIOs. */ 27 + struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */ 28 + /* 29 + * Speed conversion array: rpm from/to GPIO bit field. 30 + * This array _must_ be sorted in ascending rpm order. 31 + */ 32 + int num_speed; 33 + struct gpio_fan_speed *speed; 34 + }; 35 + 36 + #endif /* __LINUX_GPIO_FAN_H */