diff options
author | Guenter Roeck <linux@roeck-us.net> | 2012-12-04 10:56:24 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2013-04-08 00:16:39 -0400 |
commit | 1c65dc365ed38d6839fcc231ea38a6163fb9d343 (patch) | |
tree | 98bab52616fa4fbd955179416eaaf118a1616542 /drivers/hwmon | |
parent | aa136e5dad9fbec9e98867278555a81f2d75ea10 (diff) |
hwmon: (nct6775) Add support for fan speed attributes
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/nct6775.c | 516 |
1 files changed, 515 insertions, 1 deletions
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index fd0dd15ae4b6..bafcae55e255 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c | |||
@@ -180,6 +180,9 @@ static const u16 NCT6775_REG_IN[] = { | |||
180 | #define NCT6775_REG_VBAT 0x5D | 180 | #define NCT6775_REG_VBAT 0x5D |
181 | #define NCT6775_REG_DIODE 0x5E | 181 | #define NCT6775_REG_DIODE 0x5E |
182 | 182 | ||
183 | #define NCT6775_REG_FANDIV1 0x506 | ||
184 | #define NCT6775_REG_FANDIV2 0x507 | ||
185 | |||
183 | static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; | 186 | static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; |
184 | 187 | ||
185 | /* 0..15 voltages, 16..23 fans, 24..31 temperatures */ | 188 | /* 0..15 voltages, 16..23 fans, 24..31 temperatures */ |
@@ -193,12 +196,16 @@ static const s8 NCT6775_ALARM_BITS[] = { | |||
193 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ | 196 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ |
194 | 12, -1 }; /* intrusion0, intrusion1 */ | 197 | 12, -1 }; /* intrusion0, intrusion1 */ |
195 | 198 | ||
199 | #define FAN_ALARM_BASE 16 | ||
196 | #define TEMP_ALARM_BASE 24 | 200 | #define TEMP_ALARM_BASE 24 |
197 | #define INTRUSION_ALARM_BASE 30 | 201 | #define INTRUSION_ALARM_BASE 30 |
198 | 202 | ||
199 | static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; | 203 | static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; |
200 | static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; | 204 | static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; |
201 | 205 | ||
206 | static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; | ||
207 | static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; | ||
208 | |||
202 | static const u16 NCT6775_REG_TEMP[] = { | 209 | static const u16 NCT6775_REG_TEMP[] = { |
203 | 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; | 210 | 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; |
204 | 211 | ||
@@ -256,6 +263,8 @@ static const s8 NCT6776_ALARM_BITS[] = { | |||
256 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ | 263 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ |
257 | 12, 9 }; /* intrusion0, intrusion1 */ | 264 | 12, 9 }; /* intrusion0, intrusion1 */ |
258 | 265 | ||
266 | static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; | ||
267 | |||
259 | static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { | 268 | static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { |
260 | 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; | 269 | 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; |
261 | 270 | ||
@@ -309,6 +318,8 @@ static const s8 NCT6779_ALARM_BITS[] = { | |||
309 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ | 318 | 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ |
310 | 12, 9 }; /* intrusion0, intrusion1 */ | 319 | 12, 9 }; /* intrusion0, intrusion1 */ |
311 | 320 | ||
321 | static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; | ||
322 | |||
312 | static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; | 323 | static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; |
313 | static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { | 324 | static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { |
314 | 0x18, 0x152 }; | 325 | 0x18, 0x152 }; |
@@ -363,6 +374,44 @@ static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] | |||
363 | * Conversions | 374 | * Conversions |
364 | */ | 375 | */ |
365 | 376 | ||
377 | static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) | ||
378 | { | ||
379 | if (reg == 0 || reg == 255) | ||
380 | return 0; | ||
381 | return 1350000U / (reg << divreg); | ||
382 | } | ||
383 | |||
384 | static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) | ||
385 | { | ||
386 | if ((reg & 0xff1f) == 0xff1f) | ||
387 | return 0; | ||
388 | |||
389 | reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); | ||
390 | |||
391 | if (reg == 0) | ||
392 | return 0; | ||
393 | |||
394 | return 1350000U / reg; | ||
395 | } | ||
396 | |||
397 | static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) | ||
398 | { | ||
399 | if (reg == 0 || reg == 0xffff) | ||
400 | return 0; | ||
401 | |||
402 | /* | ||
403 | * Even though the registers are 16 bit wide, the fan divisor | ||
404 | * still applies. | ||
405 | */ | ||
406 | return 1350000U / (reg << divreg); | ||
407 | } | ||
408 | |||
409 | static inline unsigned int | ||
410 | div_from_reg(u8 reg) | ||
411 | { | ||
412 | return 1 << reg; | ||
413 | } | ||
414 | |||
366 | /* | 415 | /* |
367 | * Some of the voltage inputs have internal scaling, the tables below | 416 | * Some of the voltage inputs have internal scaling, the tables below |
368 | * contain 8 (the ADC LSB in mV) * scaling factor * 100 | 417 | * contain 8 (the ADC LSB in mV) * scaling factor * 100 |
@@ -411,12 +460,17 @@ struct nct6775_data { | |||
411 | const u16 *REG_VIN; | 460 | const u16 *REG_VIN; |
412 | const u16 *REG_IN_MINMAX[2]; | 461 | const u16 *REG_IN_MINMAX[2]; |
413 | 462 | ||
414 | const u16 *REG_TEMP_SOURCE; /* temp register sources */ | 463 | const u16 *REG_FAN; |
464 | const u16 *REG_FAN_MIN; | ||
415 | 465 | ||
466 | const u16 *REG_TEMP_SOURCE; /* temp register sources */ | ||
416 | const u16 *REG_TEMP_OFFSET; | 467 | const u16 *REG_TEMP_OFFSET; |
417 | 468 | ||
418 | const u16 *REG_ALARM; | 469 | const u16 *REG_ALARM; |
419 | 470 | ||
471 | unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); | ||
472 | unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); | ||
473 | |||
420 | struct mutex update_lock; | 474 | struct mutex update_lock; |
421 | bool valid; /* true if following fields are valid */ | 475 | bool valid; /* true if following fields are valid */ |
422 | unsigned long last_updated; /* In jiffies */ | 476 | unsigned long last_updated; /* In jiffies */ |
@@ -425,6 +479,12 @@ struct nct6775_data { | |||
425 | u8 bank; /* current register bank */ | 479 | u8 bank; /* current register bank */ |
426 | u8 in_num; /* number of in inputs we have */ | 480 | u8 in_num; /* number of in inputs we have */ |
427 | u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ | 481 | u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ |
482 | unsigned int rpm[5]; | ||
483 | u16 fan_min[5]; | ||
484 | u8 fan_div[5]; | ||
485 | u8 has_fan; /* some fan inputs can be disabled */ | ||
486 | u8 has_fan_min; /* some fans don't have min register */ | ||
487 | bool has_fan_div; | ||
428 | 488 | ||
429 | u8 temp_fixed_num; /* 3 or 6 */ | 489 | u8 temp_fixed_num; /* 3 or 6 */ |
430 | u8 temp_type[NUM_TEMP_FIXED]; | 490 | u8 temp_type[NUM_TEMP_FIXED]; |
@@ -556,6 +616,153 @@ static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) | |||
556 | return nct6775_write_value(data, reg, value); | 616 | return nct6775_write_value(data, reg, value); |
557 | } | 617 | } |
558 | 618 | ||
619 | /* This function assumes that the caller holds data->update_lock */ | ||
620 | static void nct6775_write_fan_div(struct nct6775_data *data, int nr) | ||
621 | { | ||
622 | u8 reg; | ||
623 | |||
624 | switch (nr) { | ||
625 | case 0: | ||
626 | reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70) | ||
627 | | (data->fan_div[0] & 0x7); | ||
628 | nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); | ||
629 | break; | ||
630 | case 1: | ||
631 | reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7) | ||
632 | | ((data->fan_div[1] << 4) & 0x70); | ||
633 | nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); | ||
634 | break; | ||
635 | case 2: | ||
636 | reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70) | ||
637 | | (data->fan_div[2] & 0x7); | ||
638 | nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); | ||
639 | break; | ||
640 | case 3: | ||
641 | reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7) | ||
642 | | ((data->fan_div[3] << 4) & 0x70); | ||
643 | nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); | ||
644 | break; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr) | ||
649 | { | ||
650 | if (data->kind == nct6775) | ||
651 | nct6775_write_fan_div(data, nr); | ||
652 | } | ||
653 | |||
654 | static void nct6775_update_fan_div(struct nct6775_data *data) | ||
655 | { | ||
656 | u8 i; | ||
657 | |||
658 | i = nct6775_read_value(data, NCT6775_REG_FANDIV1); | ||
659 | data->fan_div[0] = i & 0x7; | ||
660 | data->fan_div[1] = (i & 0x70) >> 4; | ||
661 | i = nct6775_read_value(data, NCT6775_REG_FANDIV2); | ||
662 | data->fan_div[2] = i & 0x7; | ||
663 | if (data->has_fan & (1<<3)) | ||
664 | data->fan_div[3] = (i & 0x70) >> 4; | ||
665 | } | ||
666 | |||
667 | static void nct6775_update_fan_div_common(struct nct6775_data *data) | ||
668 | { | ||
669 | if (data->kind == nct6775) | ||
670 | nct6775_update_fan_div(data); | ||
671 | } | ||
672 | |||
673 | static void nct6775_init_fan_div(struct nct6775_data *data) | ||
674 | { | ||
675 | int i; | ||
676 | |||
677 | nct6775_update_fan_div_common(data); | ||
678 | /* | ||
679 | * For all fans, start with highest divider value if the divider | ||
680 | * register is not initialized. This ensures that we get a | ||
681 | * reading from the fan count register, even if it is not optimal. | ||
682 | * We'll compute a better divider later on. | ||
683 | */ | ||
684 | for (i = 0; i < 3; i++) { | ||
685 | if (!(data->has_fan & (1 << i))) | ||
686 | continue; | ||
687 | if (data->fan_div[i] == 0) { | ||
688 | data->fan_div[i] = 7; | ||
689 | nct6775_write_fan_div_common(data, i); | ||
690 | } | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static void nct6775_init_fan_common(struct device *dev, | ||
695 | struct nct6775_data *data) | ||
696 | { | ||
697 | int i; | ||
698 | u8 reg; | ||
699 | |||
700 | if (data->has_fan_div) | ||
701 | nct6775_init_fan_div(data); | ||
702 | |||
703 | /* | ||
704 | * If fan_min is not set (0), set it to 0xff to disable it. This | ||
705 | * prevents the unnecessary warning when fanX_min is reported as 0. | ||
706 | */ | ||
707 | for (i = 0; i < 5; i++) { | ||
708 | if (data->has_fan_min & (1 << i)) { | ||
709 | reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); | ||
710 | if (!reg) | ||
711 | nct6775_write_value(data, data->REG_FAN_MIN[i], | ||
712 | data->has_fan_div ? 0xff | ||
713 | : 0xff1f); | ||
714 | } | ||
715 | } | ||
716 | } | ||
717 | |||
718 | static void nct6775_select_fan_div(struct device *dev, | ||
719 | struct nct6775_data *data, int nr, u16 reg) | ||
720 | { | ||
721 | u8 fan_div = data->fan_div[nr]; | ||
722 | u16 fan_min; | ||
723 | |||
724 | if (!data->has_fan_div) | ||
725 | return; | ||
726 | |||
727 | /* | ||
728 | * If we failed to measure the fan speed, or the reported value is not | ||
729 | * in the optimal range, and the clock divider can be modified, | ||
730 | * let's try that for next time. | ||
731 | */ | ||
732 | if (reg == 0x00 && fan_div < 0x07) | ||
733 | fan_div++; | ||
734 | else if (reg != 0x00 && reg < 0x30 && fan_div > 0) | ||
735 | fan_div--; | ||
736 | |||
737 | if (fan_div != data->fan_div[nr]) { | ||
738 | dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n", | ||
739 | nr + 1, div_from_reg(data->fan_div[nr]), | ||
740 | div_from_reg(fan_div)); | ||
741 | |||
742 | /* Preserve min limit if possible */ | ||
743 | if (data->has_fan_min & (1 << nr)) { | ||
744 | fan_min = data->fan_min[nr]; | ||
745 | if (fan_div > data->fan_div[nr]) { | ||
746 | if (fan_min != 255 && fan_min > 1) | ||
747 | fan_min >>= 1; | ||
748 | } else { | ||
749 | if (fan_min != 255) { | ||
750 | fan_min <<= 1; | ||
751 | if (fan_min > 254) | ||
752 | fan_min = 254; | ||
753 | } | ||
754 | } | ||
755 | if (fan_min != data->fan_min[nr]) { | ||
756 | data->fan_min[nr] = fan_min; | ||
757 | nct6775_write_value(data, data->REG_FAN_MIN[nr], | ||
758 | fan_min); | ||
759 | } | ||
760 | } | ||
761 | data->fan_div[nr] = fan_div; | ||
762 | nct6775_write_fan_div_common(data, nr); | ||
763 | } | ||
764 | } | ||
765 | |||
559 | static struct nct6775_data *nct6775_update_device(struct device *dev) | 766 | static struct nct6775_data *nct6775_update_device(struct device *dev) |
560 | { | 767 | { |
561 | struct nct6775_data *data = dev_get_drvdata(dev); | 768 | struct nct6775_data *data = dev_get_drvdata(dev); |
@@ -565,6 +772,9 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) | |||
565 | 772 | ||
566 | if (time_after(jiffies, data->last_updated + HZ + HZ/2) | 773 | if (time_after(jiffies, data->last_updated + HZ + HZ/2) |
567 | || !data->valid) { | 774 | || !data->valid) { |
775 | /* Fan clock dividers */ | ||
776 | nct6775_update_fan_div_common(data); | ||
777 | |||
568 | /* Measured voltages and limits */ | 778 | /* Measured voltages and limits */ |
569 | for (i = 0; i < data->in_num; i++) { | 779 | for (i = 0; i < data->in_num; i++) { |
570 | if (!(data->have_in & (1 << i))) | 780 | if (!(data->have_in & (1 << i))) |
@@ -578,6 +788,24 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) | |||
578 | data->REG_IN_MINMAX[1][i]); | 788 | data->REG_IN_MINMAX[1][i]); |
579 | } | 789 | } |
580 | 790 | ||
791 | /* Measured fan speeds and limits */ | ||
792 | for (i = 0; i < 5; i++) { | ||
793 | u16 reg; | ||
794 | |||
795 | if (!(data->has_fan & (1 << i))) | ||
796 | continue; | ||
797 | |||
798 | reg = nct6775_read_value(data, data->REG_FAN[i]); | ||
799 | data->rpm[i] = data->fan_from_reg(reg, | ||
800 | data->fan_div[i]); | ||
801 | |||
802 | if (data->has_fan_min & (1 << i)) | ||
803 | data->fan_min[i] = nct6775_read_value(data, | ||
804 | data->REG_FAN_MIN[i]); | ||
805 | |||
806 | nct6775_select_fan_div(dev, data, i, reg); | ||
807 | } | ||
808 | |||
581 | /* Measured temperatures and limits */ | 809 | /* Measured temperatures and limits */ |
582 | for (i = 0; i < NUM_TEMP; i++) { | 810 | for (i = 0; i < NUM_TEMP; i++) { |
583 | if (!(data->have_temp & (1 << i))) | 811 | if (!(data->have_temp & (1 << i))) |
@@ -875,6 +1103,166 @@ static const struct attribute_group nct6775_group_in[15] = { | |||
875 | }; | 1103 | }; |
876 | 1104 | ||
877 | static ssize_t | 1105 | static ssize_t |
1106 | show_fan(struct device *dev, struct device_attribute *attr, char *buf) | ||
1107 | { | ||
1108 | struct nct6775_data *data = nct6775_update_device(dev); | ||
1109 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); | ||
1110 | int nr = sattr->index; | ||
1111 | return sprintf(buf, "%d\n", data->rpm[nr]); | ||
1112 | } | ||
1113 | |||
1114 | static ssize_t | ||
1115 | show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) | ||
1116 | { | ||
1117 | struct nct6775_data *data = nct6775_update_device(dev); | ||
1118 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); | ||
1119 | int nr = sattr->index; | ||
1120 | return sprintf(buf, "%d\n", | ||
1121 | data->fan_from_reg_min(data->fan_min[nr], | ||
1122 | data->fan_div[nr])); | ||
1123 | } | ||
1124 | |||
1125 | static ssize_t | ||
1126 | show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) | ||
1127 | { | ||
1128 | struct nct6775_data *data = nct6775_update_device(dev); | ||
1129 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); | ||
1130 | int nr = sattr->index; | ||
1131 | return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); | ||
1132 | } | ||
1133 | |||
1134 | static ssize_t | ||
1135 | store_fan_min(struct device *dev, struct device_attribute *attr, | ||
1136 | const char *buf, size_t count) | ||
1137 | { | ||
1138 | struct nct6775_data *data = dev_get_drvdata(dev); | ||
1139 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); | ||
1140 | int nr = sattr->index; | ||
1141 | unsigned long val; | ||
1142 | int err; | ||
1143 | unsigned int reg; | ||
1144 | u8 new_div; | ||
1145 | |||
1146 | err = kstrtoul(buf, 10, &val); | ||
1147 | if (err < 0) | ||
1148 | return err; | ||
1149 | |||
1150 | mutex_lock(&data->update_lock); | ||
1151 | if (!data->has_fan_div) { | ||
1152 | /* NCT6776F or NCT6779D; we know this is a 13 bit register */ | ||
1153 | if (!val) { | ||
1154 | val = 0xff1f; | ||
1155 | } else { | ||
1156 | if (val > 1350000U) | ||
1157 | val = 135000U; | ||
1158 | val = 1350000U / val; | ||
1159 | val = (val & 0x1f) | ((val << 3) & 0xff00); | ||
1160 | } | ||
1161 | data->fan_min[nr] = val; | ||
1162 | goto write_min; /* Leave fan divider alone */ | ||
1163 | } | ||
1164 | if (!val) { | ||
1165 | /* No min limit, alarm disabled */ | ||
1166 | data->fan_min[nr] = 255; | ||
1167 | new_div = data->fan_div[nr]; /* No change */ | ||
1168 | dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); | ||
1169 | goto write_div; | ||
1170 | } | ||
1171 | reg = 1350000U / val; | ||
1172 | if (reg >= 128 * 255) { | ||
1173 | /* | ||
1174 | * Speed below this value cannot possibly be represented, | ||
1175 | * even with the highest divider (128) | ||
1176 | */ | ||
1177 | data->fan_min[nr] = 254; | ||
1178 | new_div = 7; /* 128 == (1 << 7) */ | ||
1179 | dev_warn(dev, | ||
1180 | "fan%u low limit %lu below minimum %u, set to minimum\n", | ||
1181 | nr + 1, val, data->fan_from_reg_min(254, 7)); | ||
1182 | } else if (!reg) { | ||
1183 | /* | ||
1184 | * Speed above this value cannot possibly be represented, | ||
1185 | * even with the lowest divider (1) | ||
1186 | */ | ||
1187 | data->fan_min[nr] = 1; | ||
1188 | new_div = 0; /* 1 == (1 << 0) */ | ||
1189 | dev_warn(dev, | ||
1190 | "fan%u low limit %lu above maximum %u, set to maximum\n", | ||
1191 | nr + 1, val, data->fan_from_reg_min(1, 0)); | ||
1192 | } else { | ||
1193 | /* | ||
1194 | * Automatically pick the best divider, i.e. the one such | ||
1195 | * that the min limit will correspond to a register value | ||
1196 | * in the 96..192 range | ||
1197 | */ | ||
1198 | new_div = 0; | ||
1199 | while (reg > 192 && new_div < 7) { | ||
1200 | reg >>= 1; | ||
1201 | new_div++; | ||
1202 | } | ||
1203 | data->fan_min[nr] = reg; | ||
1204 | } | ||
1205 | |||
1206 | write_div: | ||
1207 | /* | ||
1208 | * Write both the fan clock divider (if it changed) and the new | ||
1209 | * fan min (unconditionally) | ||
1210 | */ | ||
1211 | if (new_div != data->fan_div[nr]) { | ||
1212 | dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", | ||
1213 | nr + 1, div_from_reg(data->fan_div[nr]), | ||
1214 | div_from_reg(new_div)); | ||
1215 | data->fan_div[nr] = new_div; | ||
1216 | nct6775_write_fan_div_common(data, nr); | ||
1217 | /* Give the chip time to sample a new speed value */ | ||
1218 | data->last_updated = jiffies; | ||
1219 | } | ||
1220 | |||
1221 | write_min: | ||
1222 | nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); | ||
1223 | mutex_unlock(&data->update_lock); | ||
1224 | |||
1225 | return count; | ||
1226 | } | ||
1227 | |||
1228 | static struct sensor_device_attribute sda_fan_input[] = { | ||
1229 | SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), | ||
1230 | SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), | ||
1231 | SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), | ||
1232 | SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), | ||
1233 | SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), | ||
1234 | }; | ||
1235 | |||
1236 | static struct sensor_device_attribute sda_fan_alarm[] = { | ||
1237 | SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE), | ||
1238 | SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1), | ||
1239 | SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2), | ||
1240 | SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3), | ||
1241 | SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4), | ||
1242 | }; | ||
1243 | |||
1244 | static struct sensor_device_attribute sda_fan_min[] = { | ||
1245 | SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, | ||
1246 | store_fan_min, 0), | ||
1247 | SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, | ||
1248 | store_fan_min, 1), | ||
1249 | SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, | ||
1250 | store_fan_min, 2), | ||
1251 | SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, | ||
1252 | store_fan_min, 3), | ||
1253 | SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, | ||
1254 | store_fan_min, 4), | ||
1255 | }; | ||
1256 | |||
1257 | static struct sensor_device_attribute sda_fan_div[] = { | ||
1258 | SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), | ||
1259 | SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), | ||
1260 | SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), | ||
1261 | SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3), | ||
1262 | SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), | ||
1263 | }; | ||
1264 | |||
1265 | static ssize_t | ||
878 | show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) | 1266 | show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) |
879 | { | 1267 | { |
880 | struct nct6775_data *data = nct6775_update_device(dev); | 1268 | struct nct6775_data *data = nct6775_update_device(dev); |
@@ -1228,6 +1616,12 @@ static void nct6775_device_remove_files(struct device *dev) | |||
1228 | for (i = 0; i < data->in_num; i++) | 1616 | for (i = 0; i < data->in_num; i++) |
1229 | sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); | 1617 | sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); |
1230 | 1618 | ||
1619 | for (i = 0; i < 5; i++) { | ||
1620 | device_remove_file(dev, &sda_fan_input[i].dev_attr); | ||
1621 | device_remove_file(dev, &sda_fan_alarm[i].dev_attr); | ||
1622 | device_remove_file(dev, &sda_fan_div[i].dev_attr); | ||
1623 | device_remove_file(dev, &sda_fan_min[i].dev_attr); | ||
1624 | } | ||
1231 | for (i = 0; i < NUM_TEMP; i++) { | 1625 | for (i = 0; i < NUM_TEMP; i++) { |
1232 | if (!(data->have_temp & (1 << i))) | 1626 | if (!(data->have_temp & (1 << i))) |
1233 | continue; | 1627 | continue; |
@@ -1294,6 +1688,75 @@ static inline void nct6775_init_device(struct nct6775_data *data) | |||
1294 | } | 1688 | } |
1295 | } | 1689 | } |
1296 | 1690 | ||
1691 | static int | ||
1692 | nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, | ||
1693 | struct nct6775_data *data) | ||
1694 | { | ||
1695 | int regval; | ||
1696 | bool fan3pin, fan3min, fan4pin, fan4min, fan5pin; | ||
1697 | int ret; | ||
1698 | |||
1699 | ret = superio_enter(sio_data->sioreg); | ||
1700 | if (ret) | ||
1701 | return ret; | ||
1702 | |||
1703 | /* fan4 and fan5 share some pins with the GPIO and serial flash */ | ||
1704 | if (data->kind == nct6775) { | ||
1705 | regval = superio_inb(sio_data->sioreg, 0x2c); | ||
1706 | |||
1707 | fan3pin = regval & (1 << 6); | ||
1708 | fan3min = fan3pin; | ||
1709 | |||
1710 | /* On NCT6775, fan4 shares pins with the fdc interface */ | ||
1711 | fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); | ||
1712 | fan4min = 0; | ||
1713 | fan5pin = 0; | ||
1714 | } else if (data->kind == nct6776) { | ||
1715 | bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; | ||
1716 | |||
1717 | superio_select(sio_data->sioreg, NCT6775_LD_HWM); | ||
1718 | regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); | ||
1719 | |||
1720 | if (regval & 0x80) | ||
1721 | fan3pin = gpok; | ||
1722 | else | ||
1723 | fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); | ||
1724 | |||
1725 | if (regval & 0x40) | ||
1726 | fan4pin = gpok; | ||
1727 | else | ||
1728 | fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01; | ||
1729 | |||
1730 | if (regval & 0x20) | ||
1731 | fan5pin = gpok; | ||
1732 | else | ||
1733 | fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02; | ||
1734 | |||
1735 | fan4min = fan4pin; | ||
1736 | fan3min = fan3pin; | ||
1737 | } else { /* NCT6779D */ | ||
1738 | regval = superio_inb(sio_data->sioreg, 0x1c); | ||
1739 | |||
1740 | fan3pin = !(regval & (1 << 5)); | ||
1741 | fan4pin = !(regval & (1 << 6)); | ||
1742 | fan5pin = !(regval & (1 << 7)); | ||
1743 | |||
1744 | fan3min = fan3pin; | ||
1745 | fan4min = fan4pin; | ||
1746 | } | ||
1747 | |||
1748 | superio_exit(sio_data->sioreg); | ||
1749 | |||
1750 | data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ | ||
1751 | data->has_fan |= fan3pin << 2; | ||
1752 | data->has_fan_min |= fan3min << 2; | ||
1753 | |||
1754 | data->has_fan |= (fan4pin << 3) | (fan5pin << 4); | ||
1755 | data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); | ||
1756 | |||
1757 | return 0; | ||
1758 | } | ||
1759 | |||
1297 | static int nct6775_probe(struct platform_device *pdev) | 1760 | static int nct6775_probe(struct platform_device *pdev) |
1298 | { | 1761 | { |
1299 | struct device *dev = &pdev->dev; | 1762 | struct device *dev = &pdev->dev; |
@@ -1327,10 +1790,14 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1327 | switch (data->kind) { | 1790 | switch (data->kind) { |
1328 | case nct6775: | 1791 | case nct6775: |
1329 | data->in_num = 9; | 1792 | data->in_num = 9; |
1793 | data->has_fan_div = true; | ||
1330 | data->temp_fixed_num = 3; | 1794 | data->temp_fixed_num = 3; |
1331 | 1795 | ||
1332 | data->ALARM_BITS = NCT6775_ALARM_BITS; | 1796 | data->ALARM_BITS = NCT6775_ALARM_BITS; |
1333 | 1797 | ||
1798 | data->fan_from_reg = fan_from_reg16; | ||
1799 | data->fan_from_reg_min = fan_from_reg8; | ||
1800 | |||
1334 | data->temp_label = nct6775_temp_label; | 1801 | data->temp_label = nct6775_temp_label; |
1335 | data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); | 1802 | data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); |
1336 | 1803 | ||
@@ -1340,6 +1807,8 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1340 | data->REG_VIN = NCT6775_REG_IN; | 1807 | data->REG_VIN = NCT6775_REG_IN; |
1341 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; | 1808 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; |
1342 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; | 1809 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; |
1810 | data->REG_FAN = NCT6775_REG_FAN; | ||
1811 | data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; | ||
1343 | data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; | 1812 | data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; |
1344 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; | 1813 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; |
1345 | data->REG_ALARM = NCT6775_REG_ALARM; | 1814 | data->REG_ALARM = NCT6775_REG_ALARM; |
@@ -1355,10 +1824,14 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1355 | break; | 1824 | break; |
1356 | case nct6776: | 1825 | case nct6776: |
1357 | data->in_num = 9; | 1826 | data->in_num = 9; |
1827 | data->has_fan_div = false; | ||
1358 | data->temp_fixed_num = 3; | 1828 | data->temp_fixed_num = 3; |
1359 | 1829 | ||
1360 | data->ALARM_BITS = NCT6776_ALARM_BITS; | 1830 | data->ALARM_BITS = NCT6776_ALARM_BITS; |
1361 | 1831 | ||
1832 | data->fan_from_reg = fan_from_reg13; | ||
1833 | data->fan_from_reg_min = fan_from_reg13; | ||
1834 | |||
1362 | data->temp_label = nct6776_temp_label; | 1835 | data->temp_label = nct6776_temp_label; |
1363 | data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); | 1836 | data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); |
1364 | 1837 | ||
@@ -1368,6 +1841,8 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1368 | data->REG_VIN = NCT6775_REG_IN; | 1841 | data->REG_VIN = NCT6775_REG_IN; |
1369 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; | 1842 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; |
1370 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; | 1843 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; |
1844 | data->REG_FAN = NCT6775_REG_FAN; | ||
1845 | data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; | ||
1371 | data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; | 1846 | data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; |
1372 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; | 1847 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; |
1373 | data->REG_ALARM = NCT6775_REG_ALARM; | 1848 | data->REG_ALARM = NCT6775_REG_ALARM; |
@@ -1383,10 +1858,14 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1383 | break; | 1858 | break; |
1384 | case nct6779: | 1859 | case nct6779: |
1385 | data->in_num = 15; | 1860 | data->in_num = 15; |
1861 | data->has_fan_div = false; | ||
1386 | data->temp_fixed_num = 6; | 1862 | data->temp_fixed_num = 6; |
1387 | 1863 | ||
1388 | data->ALARM_BITS = NCT6779_ALARM_BITS; | 1864 | data->ALARM_BITS = NCT6779_ALARM_BITS; |
1389 | 1865 | ||
1866 | data->fan_from_reg = fan_from_reg13; | ||
1867 | data->fan_from_reg_min = fan_from_reg13; | ||
1868 | |||
1390 | data->temp_label = nct6779_temp_label; | 1869 | data->temp_label = nct6779_temp_label; |
1391 | data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); | 1870 | data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); |
1392 | 1871 | ||
@@ -1396,6 +1875,8 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1396 | data->REG_VIN = NCT6779_REG_IN; | 1875 | data->REG_VIN = NCT6779_REG_IN; |
1397 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; | 1876 | data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; |
1398 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; | 1877 | data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; |
1878 | data->REG_FAN = NCT6779_REG_FAN; | ||
1879 | data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; | ||
1399 | data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; | 1880 | data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; |
1400 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; | 1881 | data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; |
1401 | data->REG_ALARM = NCT6779_REG_ALARM; | 1882 | data->REG_ALARM = NCT6779_REG_ALARM; |
@@ -1575,6 +2056,13 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1575 | if (err) | 2056 | if (err) |
1576 | return err; | 2057 | return err; |
1577 | 2058 | ||
2059 | err = nct6775_check_fan_inputs(sio_data, data); | ||
2060 | if (err) | ||
2061 | goto exit_remove; | ||
2062 | |||
2063 | /* Read fan clock dividers immediately */ | ||
2064 | nct6775_init_fan_common(dev, data); | ||
2065 | |||
1578 | for (i = 0; i < data->in_num; i++) { | 2066 | for (i = 0; i < data->in_num; i++) { |
1579 | if (!(data->have_in & (1 << i))) | 2067 | if (!(data->have_in & (1 << i))) |
1580 | continue; | 2068 | continue; |
@@ -1583,6 +2071,32 @@ static int nct6775_probe(struct platform_device *pdev) | |||
1583 | goto exit_remove; | 2071 | goto exit_remove; |
1584 | } | 2072 | } |
1585 | 2073 | ||
2074 | for (i = 0; i < 5; i++) { | ||
2075 | if (data->has_fan & (1 << i)) { | ||
2076 | err = device_create_file(dev, | ||
2077 | &sda_fan_input[i].dev_attr); | ||
2078 | if (err) | ||
2079 | goto exit_remove; | ||
2080 | err = device_create_file(dev, | ||
2081 | &sda_fan_alarm[i].dev_attr); | ||
2082 | if (err) | ||
2083 | goto exit_remove; | ||
2084 | if (data->kind != nct6776 && | ||
2085 | data->kind != nct6779) { | ||
2086 | err = device_create_file(dev, | ||
2087 | &sda_fan_div[i].dev_attr); | ||
2088 | if (err) | ||
2089 | goto exit_remove; | ||
2090 | } | ||
2091 | if (data->has_fan_min & (1 << i)) { | ||
2092 | err = device_create_file(dev, | ||
2093 | &sda_fan_min[i].dev_attr); | ||
2094 | if (err) | ||
2095 | goto exit_remove; | ||
2096 | } | ||
2097 | } | ||
2098 | } | ||
2099 | |||
1586 | for (i = 0; i < NUM_TEMP; i++) { | 2100 | for (i = 0; i < NUM_TEMP; i++) { |
1587 | if (!(data->have_temp & (1 << i))) | 2101 | if (!(data->have_temp & (1 << i))) |
1588 | continue; | 2102 | continue; |