RFC PATCH adm1026: add auto-fan_div
Grant Coady
grant_lkml at dodo.com.au
Fri Apr 15 08:42:52 CEST 2005
On Thu, 14 Apr 2005 21:03:31 -0700, Philip Pokorny <ppokorny at penguincomputing.com> wrote:
>Go ahead and work up the patch for the adm1026. I'd like to review it,
>but since it's fresh in your mind, have at it.
>
Here 'tis:
Remove:
set_fan_div
unused macros and a function
Changed:
fan_div stored as 0..3 instead of 1, 2, 4, 8
Added:
fan_div value set from set_fan_min
fan_div adjustment in measurement section
fan_div writer
debug reports fan_min decision (auto/limit mode) and
fan_div changes
Compile tested, unable to perform hardware testing.
This script exercises the thing:
#!/bin/bash
#
# test auto fan clock divider adjustment
#
# Copyright (C) 2005 Grant Coady <gcoady at gmail.com>
#
# GPLv2 per linux/COPYING by reference
#
minpath="/sys/bus/i2c/devices/0-002d/fan1_min"
fanpath="/sys/bus/i2c/devices/0-002d/fan1_input"
read fan < $fanpath
lower=300
upper=13500000
fan_min=123
step=120
#
function test_cycle ()
{
fan_min=$lower
while test $fan_min -lt $upper; do
ww_adm9240
echo "$fan_min" > "$minpath"
sleep 2
read fan < $fanpath
(( fan_min=($fan_min * $step + 50) / 100 ))
(( fan_min=(($fan_min + 50) / 100) * 100 ))
done
fan_min=$upper
while test $fan_min -gt $lower; do
ww_adm9240
echo "$fan_min" > "$minpath"
sleep 2
read fan < $fanpath
(( fan_min=($fan_min * 100 + $step / 2) / $step ))
(( fan_min=(($fan_min + 50) / 100) * 100 ))
done
}
test_cycle
Needs an Ack from Philip.
Signed-off-by: Grant Coady <gcoady at gmail.com>
# adm1026.c | 150 ++++++++++++++++++++++++++++++++------------------------------
# 1 files changed, 78 insertions(+), 72 deletions(-)
--- linux-2.6.12-rc2-mm3/drivers/i2c/chips/adm1026.c 2005-04-11 20:54:56.000000000 +1000
+++ linux-2.6.12-rc2-mm3i/drivers/i2c/chips/adm1026.c 2005-04-15 15:43:41.000000000 +1000
@@ -132,6 +132,7 @@
#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr))
#define ADM1026_REG_FAN_DIV_0_3 0x02
#define ADM1026_REG_FAN_DIV_4_7 0x03
+#define ADM1026_REG_FAN_DIV(nr) (0x02 + (nr))
#define ADM1026_REG_DAC 0x04
#define ADM1026_REG_PWM 0x05
@@ -200,12 +201,8 @@
* and we assume a 2 pulse-per-rev fan tach signal
* 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000
*/
-#define FAN_TO_REG(val,div) ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*\
- (div)),1,254))
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*\
(div)))
-#define DIV_FROM_REG(val) (1<<(val))
-#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0)
/* Temperature is reported in 1 degC increments */
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\
@@ -304,6 +301,8 @@
static void adm1026_fixup_gpio(struct i2c_client *client);
static struct adm1026_data *adm1026_update_device(struct device *dev);
static void adm1026_init_client(struct i2c_client *client);
+static void adm1026_write_fan_div(struct i2c_client *client,
+ int nr, u8 fan_div);
static struct i2c_driver adm1026_driver = {
@@ -452,7 +451,7 @@
value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) |
(adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8);
for (i = 0;i <= 7;++i) {
- data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+ data->fan_div[i] = value & 0x03;
value >>= 2;
}
}
@@ -585,6 +584,26 @@
for (i = 0;i <= 7;++i) {
data->fan[i] =
adm1026_read_value(client, ADM1026_REG_FAN(i));
+
+ /* if fan_min off: auto fan clock divider adjust */
+ if (data->fan_min[i] == 255) {
+ int x = 0;
+
+ if (data->fan[i] > 192 &&
+ data->fan_div[i] < 3) {
+ x++;
+ }
+ if (data->fan[i] < 96 &&
+ data->fan_div[i] > 0) {
+ x--;
+ }
+ if (x != 0) {
+ data->fan_div[i] += x;
+ adm1026_write_fan_div(client, i,
+ data->fan_div[i]);
+ }
+ }
+
}
for (i = 0;i <= 2;++i) {
@@ -638,7 +657,7 @@
for (i = 0;i <= 7;++i) {
data->fan_min[i] = adm1026_read_value(client,
ADM1026_REG_FAN_MIN(i));
- data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+ data->fan_div[i] = value & 0x03;
value >>= 2;
}
@@ -856,25 +875,53 @@
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr],
- data->fan_div[nr]));
+ 1 << data->fan_div[nr]));
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct adm1026_data *data = adm1026_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
- data->fan_div[nr]));
+ 1 << data->fan_div[nr]));
}
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1026_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
+ unsigned long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
- data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]);
- adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr),
- data->fan_min[nr]);
+
+ dev_dbg(&client->dev, "auto? fan%u div %u min %3u val %5ld spd %u\n",
+ nr + 1, 1 << data->fan_div[nr],
+ data->fan_min[nr],val,data->fan[nr]);
+
+ if (val <= 1350000 / (8 * 254)) {
+ data->fan_min[nr] = 255; /* too low, disable alarm */
+
+ dev_dbg(&client->dev, "auto! fan%u div %u min %3u too low\n",
+ nr + 1, 1 << data->fan_div[nr],
+ data->fan_min[nr]);
+ } else {
+ /* calculate optimum fan clock divider to suit fan_min */
+ unsigned int new_min = 1350000U / val;
+ u8 new_div = 0;
+
+ while (new_min > 192 && new_div < 3) {
+ new_div++;
+ new_min++;
+ new_min >>= 1;
+ }
+ dev_dbg(&client->dev, "auto- fan%u div %u min %3u\n",
+ nr + 1, 1 << new_div, new_min);
+
+ data->fan_min[nr] = new_min;
+ if (new_div != data->fan_div[nr]) {
+ data->fan_div[nr] = new_div;
+ adm1026_write_fan_div(client, nr, new_div);
+ }
+ }
+
up(&data->update_lock);
return count;
}
@@ -906,64 +953,28 @@
fan_offset(7);
fan_offset(8);
-/* Adjust fan_min to account for new fan divisor */
-static void fixup_fan_min(struct device *dev, int fan, int old_div)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1026_data *data = i2c_get_clientdata(client);
- int new_min;
- int new_div = data->fan_div[fan];
-
- /* 0 and 0xff are special. Don't adjust them */
- if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) {
- return;
- }
-
- new_min = data->fan_min[fan] * old_div / new_div;
- new_min = SENSORS_LIMIT(new_min, 1, 254);
- data->fan_min[fan] = new_min;
- adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min);
+/* write new fan_div, callers must hold data->update_lock */
+static void adm1026_write_fan_div(struct i2c_client *client,
+ int nr, u8 fan_div)
+{
+ u8 reg, old;
+ u8 sel = (nr >> 2) & 1;
+ u8 shf = (nr & 3) * 2;
+
+ reg = adm1026_read_value(client, ADM1026_REG_FAN_DIV(sel));
+ old = (reg >> shf) & 3;
+ reg &= ~(0x03 << shf);
+ reg |= fan_div << shf;
+ adm1026_write_value(client, ADM1026_REG_FAN_DIV(sel), reg);
+ dev_dbg(&client->dev, "autoX fan%u old %u new %u fan_div changed\n",
+ nr + 1, 1 << old, 1 << fan_div);
}
-/* Now add fan_div read/write functions */
+/* Now add fan_div read function */
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct adm1026_data *data = adm1026_update_device(dev);
- return sprintf(buf,"%d\n", data->fan_div[nr]);
-}
-static ssize_t set_fan_div(struct device *dev, const char *buf,
- size_t count, int nr)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adm1026_data *data = i2c_get_clientdata(client);
- int val,orig_div,new_div,shift;
-
- val = simple_strtol(buf, NULL, 10);
- new_div = DIV_TO_REG(val);
- if (new_div == 0) {
- return -EINVAL;
- }
- down(&data->update_lock);
- orig_div = data->fan_div[nr];
- data->fan_div[nr] = DIV_FROM_REG(new_div);
-
- if (nr < 4) { /* 0 <= nr < 4 */
- shift = 2 * nr;
- adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3,
- ((DIV_TO_REG(orig_div) & (~(0x03 << shift))) |
- (new_div << shift)));
- } else { /* 3 < nr < 8 */
- shift = 2 * (nr - 4);
- adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7,
- ((DIV_TO_REG(orig_div) & (~(0x03 << (2 * shift)))) |
- (new_div << shift)));
- }
-
- if (data->fan_div[nr] != orig_div) {
- fixup_fan_min(dev,nr,orig_div);
- }
- up(&data->update_lock);
- return count;
+ return sprintf(buf,"%d\n", 1 << data->fan_div[nr]);
}
#define fan_offset_div(offset) \
@@ -971,13 +982,8 @@
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
-static ssize_t set_fan_##offset##_div (struct device *dev, \
- const char *buf, size_t count) \
-{ \
- return set_fan_div(dev, buf, count, offset - 1); \
-} \
-static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
- show_fan_##offset##_div, set_fan_##offset##_div);
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \
+ show_fan_##offset##_div, NULL);
fan_offset_div(1);
fan_offset_div(2);
More information about the lm-sensors
mailing list