hwmon/w83627hf: Add PWM frequency selection support
authorCarlos Olalla Martinez <com.ea@tinet.org>
Sat, 9 Jun 2007 14:11:16 +0000 (10:11 -0400)
committerMark M. Hoffman <mhoffman@lightlink.com>
Thu, 19 Jul 2007 18:22:13 +0000 (14:22 -0400)
Signed-off-by: Carlos Olalla <com.ea@tinet.org>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
drivers/hwmon/w83627hf.c

index 72aee3c0e8de145fc861e74e9d9c6d94917eb939..cd953604c38f372b307c7b70406e827b55086916 100644 (file)
@@ -220,6 +220,18 @@ static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2,
 #define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \
                                      regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1])
 
+#define W83627HF_REG_PWM_FREQ          0x5C    /* Only for the 627HF */
+
+#define W83637HF_REG_PWM_FREQ1         0x00    /* 697HF/687THF too */
+#define W83637HF_REG_PWM_FREQ2         0x02    /* 697HF/687THF too */
+#define W83637HF_REG_PWM_FREQ3         0x10    /* 687THF too */
+
+static const u8 W83637HF_REG_PWM_FREQ[] = { W83637HF_REG_PWM_FREQ1,
+                                       W83637HF_REG_PWM_FREQ2,
+                                       W83637HF_REG_PWM_FREQ3 };
+
+#define W83627HF_BASE_PWM_FREQ 46870
+
 #define W83781D_REG_I2C_ADDR 0x48
 #define W83781D_REG_I2C_SUBADDR 0x4A
 
@@ -267,6 +279,49 @@ static int TEMP_FROM_REG(u8 reg)
 
 #define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
 
+static inline unsigned long pwm_freq_from_reg_627hf(u8 reg)
+{
+       unsigned long freq;
+       freq = W83627HF_BASE_PWM_FREQ >> reg;
+       return freq;
+}
+static inline u8 pwm_freq_to_reg_627hf(unsigned long val)
+{
+       u8 i;
+       /* Only 5 dividers (1 2 4 8 16)
+          Search for the nearest available frequency */
+       for (i = 0; i < 4; i++) {
+               if (val > (((W83627HF_BASE_PWM_FREQ >> i) +
+                           (W83627HF_BASE_PWM_FREQ >> (i+1))) / 2))
+                       break;
+       }
+       return i;
+}
+
+static inline unsigned long pwm_freq_from_reg(u8 reg)
+{
+       /* Clock bit 8 -> 180 kHz or 24 MHz */
+       unsigned long clock = (reg & 0x80) ? 180000UL : 24000000UL;
+
+       reg &= 0x7f;
+       /* This should not happen but anyway... */
+       if (reg == 0)
+               reg++;
+       return (clock / (reg << 8));
+}
+static inline u8 pwm_freq_to_reg(unsigned long val)
+{
+       /* Minimum divider value is 0x01 and maximum is 0x7F */
+       if (val >= 93750)       /* The highest we can do */
+               return 0x01;
+       if (val >= 720) /* Use 24 MHz clock */
+               return (24000000UL / (val << 8));
+       if (val < 6)            /* The lowest we can do */
+               return 0xFF;
+       else                    /* Use 180 kHz clock */
+               return (0x80 | (180000UL / (val << 8)));
+}
+
 #define BEEP_MASK_FROM_REG(val)                 (val)
 #define BEEP_MASK_TO_REG(val)          ((val) & 0xffffff)
 #define BEEP_ENABLE_TO_REG(val)                ((val)?1:0)
@@ -316,6 +371,7 @@ struct w83627hf_data {
        u32 beep_mask;          /* Register encoding, combined */
        u8 beep_enable;         /* Boolean */
        u8 pwm[3];              /* Register value */
+       u8 pwm_freq[3];         /* Register value */
        u16 sens[3];            /* 782D/783S only.
                                   1 = pentium diode; 2 = 3904 diode;
                                   3000-5000 = thermistor beta.
@@ -851,6 +907,64 @@ sysfs_pwm(1);
 sysfs_pwm(2);
 sysfs_pwm(3);
 
+static ssize_t
+show_pwm_freq_reg(struct device *dev, char *buf, int nr)
+{
+       struct w83627hf_data *data = w83627hf_update_device(dev);
+       if (data->type == w83627hf)
+               return sprintf(buf, "%ld\n",
+                       pwm_freq_from_reg_627hf(data->pwm_freq[nr - 1]));
+       else
+               return sprintf(buf, "%ld\n",
+                       pwm_freq_from_reg(data->pwm_freq[nr - 1]));
+}
+
+static ssize_t
+store_pwm_freq_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+       struct w83627hf_data *data = dev_get_drvdata(dev);
+       static const u8 mask[]={0xF8, 0x8F};
+       u32 val;
+
+       val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+
+       if (data->type == w83627hf) {
+               data->pwm_freq[nr - 1] = pwm_freq_to_reg_627hf(val);
+               w83627hf_write_value(data, W83627HF_REG_PWM_FREQ,
+                               (data->pwm_freq[nr - 1] << ((nr - 1)*4)) |
+                               (w83627hf_read_value(data,
+                               W83627HF_REG_PWM_FREQ) & mask[nr - 1]));
+       } else {
+               data->pwm_freq[nr - 1] = pwm_freq_to_reg(val);
+               w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr - 1],
+                               data->pwm_freq[nr - 1]);
+       }
+
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+#define sysfs_pwm_freq(offset) \
+static ssize_t show_regs_pwm_freq_##offset(struct device *dev, \
+               struct device_attribute *attr, char *buf) \
+{ \
+       return show_pwm_freq_reg(dev, buf, offset); \
+} \
+static ssize_t \
+store_regs_pwm_freq_##offset(struct device *dev, \
+               struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+       return store_pwm_freq_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \
+                 show_regs_pwm_freq_##offset, store_regs_pwm_freq_##offset);
+
+sysfs_pwm_freq(1);
+sysfs_pwm_freq(2);
+sysfs_pwm_freq(3);
+
 static ssize_t
 show_sensor_reg(struct device *dev, char *buf, int nr)
 {
@@ -1077,6 +1191,9 @@ static struct attribute *w83627hf_attributes_opt[] = {
 
        &dev_attr_pwm3.attr,
 
+       &dev_attr_pwm1_freq.attr,
+       &dev_attr_pwm2_freq.attr,
+       &dev_attr_pwm3_freq.attr,
        NULL
 };
 
@@ -1139,7 +1256,9 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
                 || (err = device_create_file(dev, &dev_attr_in5_max))
                 || (err = device_create_file(dev, &dev_attr_in6_input))
                 || (err = device_create_file(dev, &dev_attr_in6_min))
-                || (err = device_create_file(dev, &dev_attr_in6_max)))
+                || (err = device_create_file(dev, &dev_attr_in6_max))
+                || (err = device_create_file(dev, &dev_attr_pwm1_freq))
+                || (err = device_create_file(dev, &dev_attr_pwm2_freq)))
                        goto ERROR4;
 
        if (data->type != w83697hf)
@@ -1169,6 +1288,12 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
                if ((err = device_create_file(dev, &dev_attr_pwm3)))
                        goto ERROR4;
 
+       if (data->type == w83637hf || data->type == w83687thf)
+               if ((err = device_create_file(dev, &dev_attr_pwm1_freq))
+                || (err = device_create_file(dev, &dev_attr_pwm2_freq))
+                || (err = device_create_file(dev, &dev_attr_pwm3_freq)))
+                       goto ERROR4;
+
        data->class_dev = hwmon_device_register(dev);
        if (IS_ERR(data->class_dev)) {
                err = PTR_ERR(data->class_dev);
@@ -1472,6 +1597,20 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev)
                           (data->type == w83627hf || data->type == w83697hf))
                                break;
                }
+               if (data->type == w83627hf) {
+                               u8 tmp = w83627hf_read_value(data,
+                                               W83627HF_REG_PWM_FREQ);
+                               data->pwm_freq[0] = tmp & 0x07;
+                               data->pwm_freq[1] = (tmp >> 4) & 0x07;
+               } else if (data->type != w83627thf) {
+                       for (i = 1; i <= 3; i++) {
+                               data->pwm_freq[i - 1] =
+                                       w83627hf_read_value(data,
+                                               W83637HF_REG_PWM_FREQ[i - 1]);
+                               if (i == 2 && (data->type == w83697hf))
+                                       break;
+                       }
+               }
 
                data->temp = w83627hf_read_value(data, W83781D_REG_TEMP(1));
                data->temp_max =