aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2010-08-14 15:08:58 -0400
committerJean Delvare <khali@linux-fr.org>2010-08-14 15:08:58 -0400
commit328716bc16b7077ea5f6293c7420247c570d6480 (patch)
treee96b0cf74988b4b3b05c6408d8640ba61e99f63c /drivers/hwmon
parent0d22d5835d4c82d1d03399688267f63334efd526 (diff)
hwmon: (pc87427) Add support for manual fan speed control
Add initial support for PWM outputs of the PC87427 Super-I/O chip. Only mode change and manual fan speed control are supported. Automatic mode configuration isn't supported, and won't be until at least one board is known, which makes uses of the PWM outputs. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig3
-rw-r--r--drivers/hwmon/pc87427.c271
2 files changed, 271 insertions, 3 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d50e9fa8016f..ea3d8dff684b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -711,7 +711,8 @@ config SENSORS_PC87427
711 functions of the National Semiconductor PC87427 Super-I/O chip. 711 functions of the National Semiconductor PC87427 Super-I/O chip.
712 The chip has two distinct logical devices, one for fan speed 712 The chip has two distinct logical devices, one for fan speed
713 monitoring and control, and one for voltage and temperature 713 monitoring and control, and one for voltage and temperature
714 monitoring. Only fan speed monitoring is supported right now. 714 monitoring. Only fan speed monitoring and control is supported
715 right now.
715 716
716 This driver can also be built as a module. If so, the module 717 This driver can also be built as a module. If so, the module
717 will be called pc87427. 718 will be called pc87427.
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c
index 0ac55ba6c6b8..869822785b4f 100644
--- a/drivers/hwmon/pc87427.c
+++ b/drivers/hwmon/pc87427.c
@@ -15,10 +15,10 @@
15 * Supports the following chips: 15 * Supports the following chips:
16 * 16 *
17 * Chip #vin #fan #pwm #temp devid 17 * Chip #vin #fan #pwm #temp devid
18 * PC87427 - 8 - - 0xF2 18 * PC87427 - 8 4 - 0xF2
19 * 19 *
20 * This driver assumes that no more than one chip is present. 20 * This driver assumes that no more than one chip is present.
21 * Only fan inputs are supported so far, although the chip can do much more. 21 * Only fans are supported so far, although the chip can do much more.
22 */ 22 */
23 23
24#include <linux/module.h> 24#include <linux/module.h>
@@ -57,10 +57,16 @@ struct pc87427_data {
57 u16 fan[8]; /* register values */ 57 u16 fan[8]; /* register values */
58 u16 fan_min[8]; /* register values */ 58 u16 fan_min[8]; /* register values */
59 u8 fan_status[8]; /* register values */ 59 u8 fan_status[8]; /* register values */
60
61 u8 pwm_enabled; /* bit vector */
62 u8 pwm_auto_ok; /* bit vector */
63 u8 pwm_enable[4]; /* register values */
64 u8 pwm[4]; /* register values */
60}; 65};
61 66
62struct pc87427_sio_data { 67struct pc87427_sio_data {
63 u8 has_fanin; 68 u8 has_fanin;
69 u8 has_fanout;
64}; 70};
65 71
66/* 72/*
@@ -72,7 +78,9 @@ struct pc87427_sio_data {
72#define SIOREG_CF2 0x22 /* Configuration 2 */ 78#define SIOREG_CF2 0x22 /* Configuration 2 */
73#define SIOREG_CF3 0x23 /* Configuration 3 */ 79#define SIOREG_CF3 0x23 /* Configuration 3 */
74#define SIOREG_CF4 0x24 /* Configuration 4 */ 80#define SIOREG_CF4 0x24 /* Configuration 4 */
81#define SIOREG_CF5 0x25 /* Configuration 5 */
75#define SIOREG_CFB 0x2B /* Configuration B */ 82#define SIOREG_CFB 0x2B /* Configuration B */
83#define SIOREG_CFC 0x2C /* Configuration C */
76#define SIOREG_CFD 0x2D /* Configuration D */ 84#define SIOREG_CFD 0x2D /* Configuration D */
77#define SIOREG_ACT 0x30 /* Device activation */ 85#define SIOREG_ACT 0x30 /* Device activation */
78#define SIOREG_MAP 0x50 /* I/O or memory mapping */ 86#define SIOREG_MAP 0x50 /* I/O or memory mapping */
@@ -188,6 +196,61 @@ static inline u16 fan_to_reg(unsigned long val)
188} 196}
189 197
190/* 198/*
199 * PWM registers and conversions
200 */
201
202#define PC87427_REG_PWM_ENABLE 0x10
203#define PC87427_REG_PWM_DUTY 0x12
204
205#define PWM_ENABLE_MODE_MASK (7 << 4)
206#define PWM_ENABLE_CTLEN (1 << 0)
207
208#define PWM_MODE_MANUAL (0 << 4)
209#define PWM_MODE_AUTO (1 << 4)
210#define PWM_MODE_OFF (2 << 4)
211#define PWM_MODE_ON (7 << 4)
212
213/* Dedicated function to read all registers related to a given PWM output.
214 This saves us quite a few locks and bank selections.
215 Must be called with data->lock held.
216 nr is from 0 to 3 */
217static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr)
218{
219 int iobase = data->address[LD_FAN];
220
221 outb(BANK_FC(nr), iobase + PC87427_REG_BANK);
222 data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE);
223 data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY);
224}
225
226static inline int pwm_enable_from_reg(u8 reg)
227{
228 switch (reg & PWM_ENABLE_MODE_MASK) {
229 case PWM_MODE_ON:
230 return 0;
231 case PWM_MODE_MANUAL:
232 case PWM_MODE_OFF:
233 return 1;
234 case PWM_MODE_AUTO:
235 return 2;
236 default:
237 return -EPROTO;
238 }
239}
240
241static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval)
242{
243 switch (val) {
244 default:
245 return PWM_MODE_ON;
246 case 1:
247 return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF;
248 case 2:
249 return PWM_MODE_AUTO;
250 }
251}
252
253/*
191 * Data interface 254 * Data interface
192 */ 255 */
193 256
@@ -207,6 +270,14 @@ static struct pc87427_data *pc87427_update_device(struct device *dev)
207 continue; 270 continue;
208 pc87427_readall_fan(data, i); 271 pc87427_readall_fan(data, i);
209 } 272 }
273
274 /* PWM outputs */
275 for (i = 0; i < 4; i++) {
276 if (!(data->pwm_enabled & (1 << i)))
277 continue;
278 pc87427_readall_pwm(data, i);
279 }
280
210 data->last_updated = jiffies; 281 data->last_updated = jiffies;
211 282
212done: 283done:
@@ -384,6 +455,145 @@ static const struct attribute_group pc87427_group_fan[8] = {
384 { .attrs = pc87427_attributes_fan[7] }, 455 { .attrs = pc87427_attributes_fan[7] },
385}; 456};
386 457
458/* Must be called with data->lock held and pc87427_readall_pwm() freshly
459 called */
460static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode)
461{
462 int iobase = data->address[LD_FAN];
463 data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK;
464 data->pwm_enable[nr] |= mode;
465 outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE);
466}
467
468static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
469 *devattr, char *buf)
470{
471 struct pc87427_data *data = pc87427_update_device(dev);
472 int nr = to_sensor_dev_attr(devattr)->index;
473 int pwm_enable;
474
475 pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]);
476 if (pwm_enable < 0)
477 return pwm_enable;
478 return sprintf(buf, "%d\n", pwm_enable);
479}
480
481static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
482 *devattr, const char *buf, size_t count)
483{
484 struct pc87427_data *data = dev_get_drvdata(dev);
485 int nr = to_sensor_dev_attr(devattr)->index;
486 unsigned long val;
487
488 if (strict_strtoul(buf, 10, &val) < 0 || val > 2)
489 return -EINVAL;
490 /* Can't go to automatic mode if it isn't configured */
491 if (val == 2 && !(data->pwm_auto_ok & (1 << nr)))
492 return -EINVAL;
493
494 mutex_lock(&data->lock);
495 pc87427_readall_pwm(data, nr);
496 update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr]));
497 mutex_unlock(&data->lock);
498
499 return count;
500}
501
502static ssize_t show_pwm(struct device *dev, struct device_attribute
503 *devattr, char *buf)
504{
505 struct pc87427_data *data = pc87427_update_device(dev);
506 int nr = to_sensor_dev_attr(devattr)->index;
507
508 return sprintf(buf, "%d\n", (int)data->pwm[nr]);
509}
510
511static ssize_t set_pwm(struct device *dev, struct device_attribute
512 *devattr, const char *buf, size_t count)
513{
514 struct pc87427_data *data = dev_get_drvdata(dev);
515 int nr = to_sensor_dev_attr(devattr)->index;
516 unsigned long val;
517 int iobase = data->address[LD_FAN];
518 u8 mode;
519
520 if (strict_strtoul(buf, 10, &val) < 0 || val > 0xff)
521 return -EINVAL;
522
523 mutex_lock(&data->lock);
524 pc87427_readall_pwm(data, nr);
525 mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
526 if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
527 dev_notice(dev, "Can't set PWM%d duty cycle while not in "
528 "manual mode\n", nr + 1);
529 mutex_unlock(&data->lock);
530 return -EPERM;
531 }
532
533 /* We may have to change the mode */
534 if (mode == PWM_MODE_MANUAL && val == 0) {
535 /* Transition from Manual to Off */
536 update_pwm_enable(data, nr, PWM_MODE_OFF);
537 mode = PWM_MODE_OFF;
538 dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
539 "manual", "off");
540 } else if (mode == PWM_MODE_OFF && val != 0) {
541 /* Transition from Off to Manual */
542 update_pwm_enable(data, nr, PWM_MODE_MANUAL);
543 mode = PWM_MODE_MANUAL;
544 dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
545 "off", "manual");
546 }
547
548 data->pwm[nr] = val;
549 if (mode == PWM_MODE_MANUAL)
550 outb(val, iobase + PC87427_REG_PWM_DUTY);
551 mutex_unlock(&data->lock);
552
553 return count;
554}
555
556static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
557 show_pwm_enable, set_pwm_enable, 0);
558static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
559 show_pwm_enable, set_pwm_enable, 1);
560static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
561 show_pwm_enable, set_pwm_enable, 2);
562static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
563 show_pwm_enable, set_pwm_enable, 3);
564
565static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
566static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
567static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
568static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
569
570static struct attribute *pc87427_attributes_pwm[4][3] = {
571 {
572 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
573 &sensor_dev_attr_pwm1.dev_attr.attr,
574 NULL
575 }, {
576 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
577 &sensor_dev_attr_pwm2.dev_attr.attr,
578 NULL
579 }, {
580 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
581 &sensor_dev_attr_pwm3.dev_attr.attr,
582 NULL
583 }, {
584 &sensor_dev_attr_pwm4_enable.dev_attr.attr,
585 &sensor_dev_attr_pwm4.dev_attr.attr,
586 NULL
587 }
588};
589
590static const struct attribute_group pc87427_group_pwm[4] = {
591 { .attrs = pc87427_attributes_pwm[0] },
592 { .attrs = pc87427_attributes_pwm[1] },
593 { .attrs = pc87427_attributes_pwm[2] },
594 { .attrs = pc87427_attributes_pwm[3] },
595};
596
387static ssize_t show_name(struct device *dev, struct device_attribute 597static ssize_t show_name(struct device *dev, struct device_attribute
388 *devattr, char *buf) 598 *devattr, char *buf)
389{ 599{
@@ -431,6 +641,25 @@ static void __devinit pc87427_init_device(struct device *dev)
431 } 641 }
432 data->fan_enabled = sio_data->has_fanin; 642 data->fan_enabled = sio_data->has_fanin;
433 } 643 }
644
645 /* Check which PWM outputs are enabled */
646 for (i = 0; i < 4; i++) {
647 if (!(sio_data->has_fanout & (1 << i))) /* Not wired */
648 continue;
649 reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i),
650 PC87427_REG_PWM_ENABLE);
651 if (reg & PWM_ENABLE_CTLEN)
652 data->pwm_enabled |= (1 << i);
653
654 /* We don't expose an interface to reconfigure the automatic
655 fan control mode, so only allow to return to this mode if
656 it was originally set. */
657 if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) {
658 dev_dbg(dev, "PWM%d is in automatic control mode\n",
659 i + 1);
660 data->pwm_auto_ok |= (1 << i);
661 }
662 }
434} 663}
435 664
436static int __devinit pc87427_probe(struct platform_device *pdev) 665static int __devinit pc87427_probe(struct platform_device *pdev)
@@ -474,6 +703,14 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
474 if (err) 703 if (err)
475 goto exit_remove_files; 704 goto exit_remove_files;
476 } 705 }
706 for (i = 0; i < 4; i++) {
707 if (!(data->pwm_enabled & (1 << i)))
708 continue;
709 err = sysfs_create_group(&pdev->dev.kobj,
710 &pc87427_group_pwm[i]);
711 if (err)
712 goto exit_remove_files;
713 }
477 714
478 data->hwmon_dev = hwmon_device_register(&pdev->dev); 715 data->hwmon_dev = hwmon_device_register(&pdev->dev);
479 if (IS_ERR(data->hwmon_dev)) { 716 if (IS_ERR(data->hwmon_dev)) {
@@ -490,6 +727,11 @@ exit_remove_files:
490 continue; 727 continue;
491 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); 728 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
492 } 729 }
730 for (i = 0; i < 4; i++) {
731 if (!(data->pwm_enabled & (1 << i)))
732 continue;
733 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]);
734 }
493exit_release_region: 735exit_release_region:
494 release_region(res->start, resource_size(res)); 736 release_region(res->start, resource_size(res));
495exit_kfree: 737exit_kfree:
@@ -512,6 +754,11 @@ static int __devexit pc87427_remove(struct platform_device *pdev)
512 continue; 754 continue;
513 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); 755 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
514 } 756 }
757 for (i = 0; i < 4; i++) {
758 if (!(data->pwm_enabled & (1 << i)))
759 continue;
760 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_pwm[i]);
761 }
515 platform_set_drvdata(pdev, NULL); 762 platform_set_drvdata(pdev, NULL);
516 kfree(data); 763 kfree(data);
517 764
@@ -648,6 +895,26 @@ static int __init pc87427_find(int sioaddr, unsigned short *address,
648 if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5))) 895 if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5)))
649 sio_data->has_fanin |= (1 << 6); /* FANIN6 */ 896 sio_data->has_fanin |= (1 << 6); /* FANIN6 */
650 897
898 /* Check which fan outputs are wired */
899 sio_data->has_fanout = (1 << 0); /* FANOUT0 */
900 if (cfg_b & (1 << 0))
901 sio_data->has_fanout |= (1 << 3); /* FANOUT3 */
902
903 cfg = superio_inb(sioaddr, SIOREG_CFC);
904 if (!(cfg & (1 << 4))) {
905 if (cfg_b & (1 << 1))
906 sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
907 if (cfg_b & (1 << 2))
908 sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
909 }
910
911 /* FANOUT1 and FANOUT2 can each be routed to 2 different pins */
912 cfg = superio_inb(sioaddr, SIOREG_CF5);
913 if (cfg & (1 << 6))
914 sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
915 if (cfg & (1 << 5))
916 sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
917
651exit: 918exit:
652 superio_exit(sioaddr); 919 superio_exit(sioaddr);
653 return err; 920 return err;