diff options
Diffstat (limited to 'drivers/hwmon/it87.c')
-rw-r--r-- | drivers/hwmon/it87.c | 254 |
1 files changed, 185 insertions, 69 deletions
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 25763d2223b6..14a5d981be7d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -15,7 +15,9 @@ * IT8716F Super I/O chip w/LPC interface * IT8718F Super I/O chip w/LPC interface * IT8720F Super I/O chip w/LPC interface + * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface + * IT8758E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -54,7 +56,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -126,6 +128,7 @@ superio_exit(void) #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 #define IT8720F_DEVID 0x8720 +#define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -202,56 +205,6 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) #define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, - 254); -} - -static inline u16 FAN16_TO_REG(long rpm) -{ - if (rpm == 0) - return 0xffff; - return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) -/* The divider is fixed to 2 in 16-bit mode */ -#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2)) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ - ((val)+500)/1000),-128,127)) -#define TEMP_FROM_REG(val) ((val) * 1000) - -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val)&0x7f) << 1) - -static int DIV_TO_REG(int val) -{ - int answer = 0; - while (answer < 7 && (val >>= 1)) - answer++; - return answer; -} -#define DIV_FROM_REG(val) (1 << (val)) - -static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, -}; - struct it87_sio_data { enum chips type; @@ -259,6 +212,7 @@ struct it87_sio_data { u8 revision; u8 vid_value; u8 beep_pin; + u8 internal; /* Internal sensors can be labeled */ /* Features skipped based on config or DMI */ u8 skip_vid; u8 skip_fan; @@ -278,6 +232,7 @@ struct it87_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ + u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[9]; /* Register value */ u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ @@ -309,6 +264,96 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; +static u8 in_to_reg(const struct it87_data *data, int nr, long val) +{ + long lsb; + + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + lsb = 24; + else + lsb = 12; + } else + lsb = 16; + + val = DIV_ROUND_CLOSEST(val, lsb); + return SENSORS_LIMIT(val, 0, 255); +} + +static int in_from_reg(const struct it87_data *data, int nr, int val) +{ + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + return val * 24; + else + return val * 12; + } else + return val * 16; +} + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +static inline u16 FAN16_TO_REG(long rpm) +{ + if (rpm == 0) + return 0xffff; + return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); +} + +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 255 ? 0 : \ + 1350000 / ((val) * (div))) +/* The divider is fixed to 2 in 16-bit mode */ +#define FAN16_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \ + 1350000 / ((val) * 2)) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (((val) - 500) / 1000) : \ + ((val) + 500) / 1000), -128, 127)) +#define TEMP_FROM_REG(val) ((val) * 1000) + +static u8 pwm_to_reg(const struct it87_data *data, long val) +{ + if (data->type == it8721) + return val; + else + return val >> 1; +} + +static int pwm_from_reg(const struct it87_data *data, u8 reg) +{ + if (data->type == it8721) + return reg; + else + return (reg & 0x7f) << 1; +} + + +static int DIV_TO_REG(int val) +{ + int answer = 0; + while (answer < 7 && (val >>= 1)) + answer++; + return answer; +} +#define DIV_FROM_REG(val) (1 << (val)) + +static const unsigned int pwm_freq[8] = { + 48000000 / 128, + 24000000 / 128, + 12000000 / 128, + 8000000 / 128, + 6000000 / 128, + 3000000 / 128, + 1500000 / 128, + 750000 / 128, +}; + static inline int has_16bit_fans(const struct it87_data *data) { /* IT8705F Datasheet 0.4.1, 3h == Version G. @@ -318,7 +363,8 @@ static inline int has_16bit_fans(const struct it87_data *data) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 || data->type == it8718 - || data->type == it8720; + || data->type == it8720 + || data->type == it8721; } static inline int has_old_autopwm(const struct it87_data *data) @@ -356,7 +402,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr])); } static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, @@ -366,7 +412,7 @@ static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_min[nr])); } static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, @@ -376,7 +422,7 @@ static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_max[nr])); } static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, @@ -392,7 +438,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); + data->in_min[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); @@ -411,7 +457,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); + data->in_max[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); @@ -641,7 +687,8 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->pwm_duty[nr])); } static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -811,7 +858,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->pwm_duty[nr] = PWM_TO_REG(val); + data->pwm_duty[nr] = pwm_to_reg(data, val); /* If we are in manual mode, write the duty cycle immediately; * otherwise, just store it for later use. */ if (!(data->pwm_ctrl[nr] & 0x80)) { @@ -915,7 +962,8 @@ static ssize_t show_auto_pwm(struct device *dev, int nr = sensor_attr->nr; int point = sensor_attr->index; - return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->auto_pwm[nr][point])); } static ssize_t set_auto_pwm(struct device *dev, @@ -932,7 +980,7 @@ static ssize_t set_auto_pwm(struct device *dev, return -EINVAL; mutex_lock(&data->update_lock); - data->auto_pwm[nr][point] = PWM_TO_REG(val); + data->auto_pwm[nr][point] = pwm_to_reg(data, val); it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), data->auto_pwm[nr][point]); mutex_unlock(&data->update_lock); @@ -1194,6 +1242,29 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); +static ssize_t show_label(struct device *dev, struct device_attribute *attr, + char *buf) +{ + static const char *labels[] = { + "+5V", + "5VSB", + "Vbat", + }; + static const char *labels_it8721[] = { + "+3.3V", + "3VSB", + "Vbat", + }; + struct it87_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr] + : labels[nr]); +} +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2); + static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1434,6 +1505,17 @@ static const struct attribute_group it87_group_vid = { .attrs = it87_attributes_vid, }; +static struct attribute *it87_attributes_label[] = { + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group it87_group_label = { + .attrs = it87_attributes_vid, +}; + /* SuperIO detection - will change isa_address if a chip is found */ static int __init it87_find(unsigned short *address, struct it87_sio_data *sio_data) @@ -1462,6 +1544,9 @@ static int __init it87_find(unsigned short *address, case IT8720F_DEVID: sio_data->type = it8720; break; + case IT8721F_DEVID: + sio_data->type = it8721; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1487,6 +1572,9 @@ static int __init it87_find(unsigned short *address, pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", chip_type, *address, sio_data->revision); + /* in8 (Vbat) is always internal */ + sio_data->internal = (1 << 2); + /* Read GPIO config and VID value from LDN 7 (GPIO) */ if (sio_data->type == it87) { /* The IT8705F doesn't have VID pins at all */ @@ -1499,11 +1587,17 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); - if (reg & 0x0f) { - pr_info("it87: VID is disabled (pins used for GPIO)\n"); + if (sio_data->type == it8721) { + /* The IT8721F/IT8758E doesn't have VID pins at all */ sio_data->skip_vid = 1; + } else { + /* We need at least 4 VID pins */ + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } } /* Check if fan3 is there or not */ @@ -1540,9 +1634,9 @@ static int __init it87_find(unsigned short *address, pr_notice("it87: Routing internal VCCH to in7\n"); } if (reg & (1 << 0)) - pr_info("it87: in3 is VCC (+5V)\n"); - if (reg & (1 << 1)) - pr_info("it87: in7 is VCCH (+5V Stand-By)\n"); + sio_data->internal |= (1 << 0); + if ((reg & (1 << 1)) || sio_data->type == it8721) + sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } @@ -1600,6 +1694,7 @@ static void it87_remove_files(struct device *dev) } if (!sio_data->skip_vid) sysfs_remove_group(&dev->kobj, &it87_group_vid); + sysfs_remove_group(&dev->kobj, &it87_group_label); } static int __devinit it87_probe(struct platform_device *pdev) @@ -1618,6 +1713,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8716", "it8718", "it8720", + "it8721", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1654,6 +1750,16 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(dev); + /* Starting with IT8721F, we handle scaling of internal voltages */ + if (data->type == it8721) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is AVCC */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VSB */ + if (sio_data->internal & (1 << 2)) + data->in_scaled |= (1 << 8); /* in8 is Vbat */ + } + /* Initialize the IT87 chip */ it87_init_device(pdev); @@ -1725,6 +1831,16 @@ static int __devinit it87_probe(struct platform_device *pdev) goto ERROR4; } + /* Export labels for internal sensors */ + for (i = 0; i < 3; i++) { + if (!(sio_data->internal & (1 << i))) + continue; + err = sysfs_create_file(&dev->kobj, + it87_attributes_label[i]); + if (err) + goto ERROR4; + } + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); @@ -2009,7 +2125,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 and the 8720 don't use IT87_REG_VID for the + The 8718 and later don't use IT87_REG_VID for the same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); @@ -2109,7 +2225,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare <khali@linux-fr.org>"); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); |