diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-24 01:37:52 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-24 01:37:52 +0400 |
commit | f37ab0fba271e43edab0e3ae9fe644fcda455402 (patch) | |
tree | 9389e993a99c7ad87c0e8643d464c48f3ac97eca /drivers | |
parent | 0b0c9d3a5872e8a02a071c6f0775ee6bf00a1206 (diff) | |
parent | 5510e62a66bad22b104d5d854445523d7f5754f7 (diff) | |
download | linux-f37ab0fba271e43edab0e3ae9fe644fcda455402.tar.xz |
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
Pull hwmon updates from Jean Delvare:
"We have support for the MCP3021, MC13892 and GMT G781, automatic fan
speed control for LM63/LM64 chips, and a few clean-ups."
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
hwmon: Add MCP3021 ADC driver
hwmon: (mc13783-adc) Add support for the MC13892 PMIC
hwmon: (mc13783-adc) Remove space before tab
hwmon: (lm63) Let the user adjust the lookup table
hwmon: (lm63) Make fan speed control strategy changeable
hwmon: (lm63) Reorganize the code
hwmon: (lm90) Restore original configuration if probe function fails
hwmon: (lm90) Add support for GMT G781
hwmon: (lm90) Fix multi-line comments
hwmon: (w83795) Fix multi-line comments
hwmon: (w83795) Unconditionally support manual fan speed control
hwmon: (fam15h_power) Increase output resolution
hwmon: (fam15h_power) Correct sign extension of running_avg_capture
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 28 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/fam15h_power.c | 10 | ||||
-rw-r--r-- | drivers/hwmon/lm63.c | 586 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 98 | ||||
-rw-r--r-- | drivers/hwmon/mc13783-adc.c | 103 | ||||
-rw-r--r-- | drivers/hwmon/mcp3021.c | 171 | ||||
-rw-r--r-- | drivers/hwmon/w83795.c | 132 |
8 files changed, 763 insertions, 366 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 811e6c47e7e6..5b32d56dbb4d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -648,7 +648,8 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -812,6 +813,16 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_MCP3021 + tristate "Microchip MCP3021" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the MCP3021 chip + that is a A/D converter (ADC) with 10-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + config SENSORS_NTC_THERMISTOR tristate "NTC thermistor support" depends on EXPERIMENTAL @@ -1229,18 +1240,19 @@ config SENSORS_W83795 depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Winbond W83795G and - W83795ADG hardware monitoring chip. + W83795ADG hardware monitoring chip, including manual fan speed + control. This driver can also be built as a module. If so, the module will be called w83795. config SENSORS_W83795_FANCTRL - boolean "Include fan control support (DANGEROUS)" + boolean "Include automatic fan control support (DANGEROUS)" depends on SENSORS_W83795 && EXPERIMENTAL default n help - If you say yes here, support for the both manual and automatic - fan control features will be included in the driver. + If you say yes here, support for automatic fan speed control + will be included in the driver. This part of the code wasn't carefully reviewed and tested yet, so enabling this option is strongly discouraged on production @@ -1358,10 +1370,10 @@ config SENSORS_APPLESMC the awesome power of applesmc. config SENSORS_MC13783_ADC - tristate "Freescale MC13783 ADC" - depends on MFD_MC13783 + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX help - Support for the A/D converter on MC13783 PMIC. + Support for the A/D converter on MC13783 and MC13892 PMIC. if ACPI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8251ce8cd035..6d3f11f71815 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 523f8fb9e7d9..b7494af1e4a9 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -60,15 +60,15 @@ static ssize_t show_power(struct device *dev, pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_RUNNING_AVERAGE, &val); running_avg_capture = (val >> 4) & 0x3fffff; - running_avg_capture = sign_extend32(running_avg_capture, 22); - running_avg_range = val & 0xf; + running_avg_capture = sign_extend32(running_avg_capture, 21); + running_avg_range = (val & 0xf) + 1; pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_LIMIT3, &val); tdp_limit = val >> 16; - curr_pwr_watts = tdp_limit + data->base_tdp - - (s32)(running_avg_capture >> (running_avg_range + 1)); + curr_pwr_watts = (tdp_limit + data->base_tdp) << running_avg_range; + curr_pwr_watts -= running_avg_capture; curr_pwr_watts *= data->tdp_to_watts; /* @@ -78,7 +78,7 @@ static ssize_t show_power(struct device *dev, * scaling factor 1/(2^16). For conversion we use * (10^6)/(2^16) = 15625/(2^10) */ - curr_pwr_watts = (curr_pwr_watts * 15625) >> 10; + curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range); return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); } static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 15c05cc83e2c..602a0f0b0de8 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -148,46 +148,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define UPDATE_INTERVAL(max, rate) \ ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max)) -/* - * Functions declaration - */ - -static int lm63_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm63_remove(struct i2c_client *client); - -static struct lm63_data *lm63_update_device(struct device *dev); - -static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm63_init_client(struct i2c_client *client); - enum chips { lm63, lm64, lm96163 }; /* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm63_id); - -static struct i2c_driver lm63_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm63", - }, - .probe = lm63_probe, - .remove = lm63_remove, - .id_table = lm63_id, - .detect = lm63_detect, - .address_list = normal_i2c, -}; - -/* * Client data (each client gets its own) */ @@ -242,6 +205,145 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr) return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); } +static inline int lut_temp_to_reg(struct lm63_data *data, long val) +{ + val -= data->temp2_offset; + if (data->lut_temp_highres) + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127500), 500); + else + return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127000), 1000); +} + +/* + * Update the lookup table register cache. + * client->update_lock must be held when calling this function. + */ +static void lm63_update_lut(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || + !data->lut_valid) { + for (i = 0; i < data->lut_size; i++) { + data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_PWM(i)); + data->temp8[3 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP(i)); + } + data->lut_temp_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP_HYST); + + data->lut_last_updated = jiffies; + data->lut_valid = 1; + } +} + +static struct lm63_data *lm63_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + + if (time_after(jiffies, next_update) || !data->valid) { + if (data->config & 0x04) { /* tachometer enabled */ + /* order matters for fan1_input */ + data->fan[0] = i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_LSB) & 0xFC; + data->fan[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_MSB) << 8; + data->fan[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_LSB) & 0xFC) + | (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_MSB) << 8); + } + + data->pwm1_freq = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + data->pwm1[0] = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); + + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_HIGH); + + /* order matters for temp2_input */ + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_MSB) << 8; + data->temp11[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_LSB); + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_LSB); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_LSB); + data->temp11[3] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_LSB); + + if (data->kind == lm96163) + data->temp11u = (i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_LSB); + + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT); + data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT_HYST); + + data->alarms = i2c_smbus_read_byte_data(client, + LM63_REG_ALERT_STATUS) & 0x7F; + + data->last_updated = jiffies; + data->valid = 1; + } + + lm63_update_lut(client); + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Trip points in the lookup table should be in ascending order for both + * temperatures and PWM output values. + */ +static int lm63_lut_looks_bad(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + int i; + + mutex_lock(&data->update_lock); + lm63_update_lut(client); + + for (i = 1; i < data->lut_size; i++) { + if (data->pwm1[1 + i - 1] > data->pwm1[1 + i] + || data->temp8[3 + i - 1] > data->temp8[3 + i]) { + dev_warn(&client->dev, + "Lookup table doesn't look sane (check entries %d and %d)\n", + i, i + 1); + break; + } + } + mutex_unlock(&data->update_lock); + + return i == data->lut_size ? 0 : 1; +} + /* * Sysfs callback functions and files */ @@ -294,13 +396,16 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", pwm); } -static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); + int nr = attr->index; unsigned long val; int err; + u8 reg; if (!(data->config_fan & 0x20)) /* register is read-only */ return -EPERM; @@ -309,11 +414,13 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, if (err) return err; + reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE; val = SENSORS_LIMIT(val, 0, 255); + mutex_lock(&data->update_lock); - data->pwm1[0] = data->pwm_highres ? val : + data->pwm1[nr] = data->pwm_highres ? val : (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]); + i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]); mutex_unlock(&data->update_lock); return count; } @@ -325,6 +432,41 @@ static ssize_t show_pwm1_enable(struct device *dev, return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); } +static ssize_t set_pwm1_enable(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val < 1 || val > 2) + return -EINVAL; + + /* + * Only let the user switch to automatic mode if the lookup table + * looks sane. + */ + if (val == 2 && lm63_lut_looks_bad(client)) + return -EPERM; + + mutex_lock(&data->update_lock); + data->config_fan = i2c_smbus_read_byte_data(client, + LM63_REG_CONFIG_FAN); + if (val == 1) + data->config_fan |= 0x20; + else + data->config_fan &= ~0x20; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, + data->config_fan); + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -367,23 +509,31 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); int nr = attr->index; - int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH; long val; int err; int temp; + u8 reg; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); - if (nr == 2) { + switch (nr) { + case 2: + reg = LM63_REG_REMOTE_TCRIT; if (data->remote_unsigned) temp = TEMP8U_TO_REG(val - data->temp2_offset); else temp = TEMP8_TO_REG(val - data->temp2_offset); - } else { + break; + case 1: + reg = LM63_REG_LOCAL_HIGH; temp = TEMP8_TO_REG(val); + break; + default: /* lookup table */ + reg = LM63_REG_LUT_TEMP(nr - 3); + temp = lut_temp_to_reg(data, val); } data->temp8[nr] = temp; i2c_smbus_write_byte_data(client, reg, temp); @@ -613,65 +763,78 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); -static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, - show_lut_temp, NULL, 3); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwm1_enable, set_pwm1_enable); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 1); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 3); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2); -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, - show_lut_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 2); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 4); static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO, - show_lut_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 5); static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO, - show_lut_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 6); static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO, - show_lut_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 7); static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6); -static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO, - show_lut_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 8); static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7); -static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO, - show_lut_temp, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 9); static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8); -static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO, - show_lut_temp, NULL, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 10); static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9); -static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO, - show_lut_temp, NULL, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 11); static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10); -static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO, - show_lut_temp, NULL, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 12); static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11); -static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO, - show_lut_temp, NULL, 13); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 13); static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 13); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12); -static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO, - show_lut_temp, NULL, 14); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 14); static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO, show_lut_temp_hyst, NULL, 14); @@ -817,28 +980,25 @@ static const struct attribute_group lm63_group_fan1 = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm63_detect(struct i2c_client *new_client, +static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; u8 man_id, chip_id, reg_config1, reg_config2; u8 reg_alert_status, reg_alert_mask; - int address = new_client->addr; + int address = client->addr; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, + reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); + reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK); if (man_id != 0x01 /* National Semiconductor */ || (reg_config1 & 0x18) != 0x00 @@ -863,74 +1023,6 @@ static int lm63_detect(struct i2c_client *new_client, return 0; } -static int lm63_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm63_data *data; - int err; - - data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Set the device type */ - data->kind = id->driver_data; - if (data->kind == lm64) - data->temp2_offset = 16000; - - /* Initialize chip */ - lm63_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm63_group); - if (err) - goto exit_free; - if (data->config & 0x04) { /* tachometer enabled */ - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_fan1); - if (err) - goto exit_remove_files; - } - if (data->kind == lm96163) { - err = device_create_file(&new_client->dev, - &dev_attr_temp2_type); - if (err) - goto exit_remove_files; - - err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - if (err) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm63_group); - sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); - if (data->kind == lm96163) { - device_remove_file(&new_client->dev, &dev_attr_temp2_type); - sysfs_remove_group(&new_client->dev.kobj, - &lm63_group_extra_lut); - } -exit_free: - kfree(data); -exit: - return err; -} - /* * Ideally we shouldn't have to initialize anything, since the BIOS * should have taken care of everything @@ -1010,114 +1102,110 @@ static void lm63_init_client(struct i2c_client *client) (data->config_fan & 0x20) ? "manual" : "auto"); } -static int lm63_remove(struct i2c_client *client) +static int lm63_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct lm63_data *data = i2c_get_clientdata(client); + struct lm63_data *data; + int err; - hwmon_device_unregister(data->hwmon_dev); + data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->valid = 0; + mutex_init(&data->update_lock); + + /* Set the device type */ + data->kind = id->driver_data; + if (data->kind == lm64) + data->temp2_offset = 16000; + + /* Initialize chip */ + lm63_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group); + if (err) + goto exit_free; + if (data->config & 0x04) { /* tachometer enabled */ + err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1); + if (err) + goto exit_remove_files; + } + if (data->kind == lm96163) { + err = device_create_file(&client->dev, &dev_attr_temp2_type); + if (err) + goto exit_remove_files; + + err = sysfs_create_group(&client->dev.kobj, + &lm63_group_extra_lut); + if (err) + goto exit_remove_files; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: sysfs_remove_group(&client->dev.kobj, &lm63_group); sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); if (data->kind == lm96163) { device_remove_file(&client->dev, &dev_attr_temp2_type); sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut); } - +exit_free: kfree(data); - return 0; +exit: + return err; } -static struct lm63_data *lm63_update_device(struct device *dev) +static int lm63_remove(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); - unsigned long next_update; - int i; - - mutex_lock(&data->update_lock); - - next_update = data->last_updated - + msecs_to_jiffies(data->update_interval) + 1; - - if (time_after(jiffies, next_update) || !data->valid) { - if (data->config & 0x04) { /* tachometer enabled */ - /* order matters for fan1_input */ - data->fan[0] = i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_LSB) & 0xFC; - data->fan[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_MSB) << 8; - data->fan[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_LSB) & 0xFC) - | (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_MSB) << 8); - } - - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - data->pwm1[0] = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); - - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_HIGH); - - /* order matters for temp2_input */ - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_MSB) << 8; - data->temp11[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_LSB); - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_LSB); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_LSB); - data->temp11[3] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_OFFSET_LSB); - - if (data->kind == lm96163) - data->temp11u = (i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM96163_REG_REMOTE_TEMP_U_LSB); - - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT); - data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT_HYST); - - data->alarms = i2c_smbus_read_byte_data(client, - LM63_REG_ALERT_STATUS) & 0x7F; - data->last_updated = jiffies; - data->valid = 1; + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm63_group); + sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); + if (data->kind == lm96163) { + device_remove_file(&client->dev, &dev_attr_temp2_type); + sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut); } - if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || - !data->lut_valid) { - for (i = 0; i < data->lut_size; i++) { - data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_PWM(i)); - data->temp8[3 + i] = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_TEMP(i)); - } - data->lut_temp_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_LUT_TEMP_HYST); + kfree(data); + return 0; +} - data->lut_last_updated = jiffies; - data->lut_valid = 1; - } +/* + * Driver data (common to all clients) + */ - mutex_unlock(&data->update_lock); +static const struct i2c_device_id lm63_id[] = { + { "lm63", lm63 }, + { "lm64", lm64 }, + { "lm96163", lm96163 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm63_id); - return data; -} +static struct i2c_driver lm63_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm63", + }, + .probe = lm63_probe, + .remove = lm63_remove, + .id_table = lm63_id, + .detect = lm63_detect, + .address_list = normal_i2c, +}; module_i2c_driver(lm63_driver); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 248f2b40dfaf..22b14a68e35e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -57,6 +57,9 @@ * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * + * This driver also supports the G781 from GMT. This device is compatible + * with the ADM1032. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -107,7 +110,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004 }; + max6646, w83l771, max6696, sa56004, g781 }; /* * The LM90 registers @@ -184,6 +187,7 @@ static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, { "adt7461a", adt7461 }, + { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, { "lm89", lm86 }, @@ -229,6 +233,12 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7c, .max_convrate = 10, }, + [g781] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7b, @@ -308,22 +318,24 @@ struct lm90_data { /* registers values */ s8 temp8[8]; /* 0: local low limit - 1: local high limit - 2: local critical limit - 3: remote critical limit - 4: local emergency limit (max6659 and max6695/96) - 5: remote emergency limit (max6659 and max6695/96) - 6: remote 2 critical limit (max6695/96 only) - 7: remote 2 emergency limit (max6695/96 only) */ + * 1: local high limit + * 2: local critical limit + * 3: remote critical limit + * 4: local emergency limit (max6659 and max6695/96) + * 5: remote emergency limit (max6659 and max6695/96) + * 6: remote 2 critical limit (max6695/96 only) + * 7: remote 2 emergency limit (max6695/96 only) + */ s16 temp11[8]; /* 0: remote input - 1: remote low limit - 2: remote high limit - 3: remote offset (except max6646, max6657/58/59, - and max6695/96) - 4: local input - 5: remote 2 input (max6695/96 only) - 6: remote 2 low limit (max6695/96 only) - 7: remote 2 high limit (ma6695/96 only) */ + * 1: remote low limit + * 2: remote high limit + * 3: remote offset (except max6646, max6657/58/59, + * and max6695/96) + * 4: local input + * 5: remote 2 input (max6695/96 only) + * 6: remote 2 low limit (max6695/96 only) + * 7: remote 2 high limit (max6695/96 only) + */ u8 temp_hyst; u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; @@ -533,8 +545,10 @@ static struct lm90_data *lm90_update_device(struct device *dev) data->alarms |= alarms << 8; } - /* Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear */ + /* + * Re-enable ALERT# output if it was originally enabled and + * relevant alarms are all clear + */ if ((data->config_orig & 0x80) == 0 && (data->alarms & data->alert_alarms) == 0) { u8 config; @@ -1162,8 +1176,10 @@ static int lm90_detect(struct i2c_client *client, && (config1 & 0x3F) == 0x00 && convrate <= 0x0A) { name = "adm1032"; - /* The ADM1032 supports PEC, but only if combined - transactions are not used. */ + /* + * The ADM1032 supports PEC, but only if combined + * transactions are not used. + */ if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) info->flags |= I2C_CLIENT_PEC; @@ -1283,6 +1299,13 @@ static int lm90_detect(struct i2c_client *client, && convrate <= 0x09) { name = "sa56004"; } + } else + if ((address == 0x4C || address == 0x4D) + && man_id == 0x47) { /* GMT */ + if (chip_id == 0x01 /* G781 */ + && (config1 & 0x3F) == 0x00 + && convrate <= 0x08) + name = "g781"; } if (!name) { /* identification failed */ @@ -1313,6 +1336,15 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) sysfs_remove_group(&dev->kobj, &lm90_group); } +static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data) +{ + /* Restore initial configuration */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + data->convrate_orig); + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + data->config_orig); +} + static void lm90_init_client(struct i2c_client *client) { u8 config, convrate; @@ -1382,8 +1414,10 @@ static int lm90_probe(struct i2c_client *client, client->flags &= ~I2C_CLIENT_PEC; } - /* Different devices have different alarm bits triggering the - * ALERT# output */ + /* + * Different devices have different alarm bits triggering the + * ALERT# output + */ data->alert_alarms = lm90_params[data->kind].alert_alarms; /* Set chip capabilities */ @@ -1399,7 +1433,7 @@ static int lm90_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &lm90_group); if (err) - goto exit_free; + goto exit_restore; if (client->flags & I2C_CLIENT_PEC) { err = device_create_file(dev, &dev_attr_pec); if (err) @@ -1438,7 +1472,8 @@ static int lm90_probe(struct i2c_client *client, exit_remove_files: lm90_remove_files(client, data); -exit_free: +exit_restore: + lm90_restore_conf(client, data); kfree(data); exit: return err; @@ -1450,12 +1485,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); lm90_remove_files(client, data); - - /* Restore initial configuration */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - data->convrate_orig); - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - data->config_orig); + lm90_restore_conf(client, data); kfree(data); return 0; @@ -1488,9 +1518,11 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) dev_warn(&client->dev, "temp%d out of range, please check!\n", 3); - /* Disable ALERT# output, because these chips don't implement - SMBus alert correctly; they should only hold the alert line - low briefly. */ + /* + * Disable ALERT# output, because these chips don't implement + * SMBus alert correctly; they should only hold the alert line + * low briefly. + */ if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index ef65ab56b094..6c6b240a782e 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -1,5 +1,5 @@ /* - * Driver for the Freescale Semiconductor MC13783 adc. + * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs. * * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2009 Sascha Hauer, Pengutronix @@ -18,7 +18,7 @@ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <linux/mfd/mc13783.h> +#include <linux/mfd/mc13xxx.h> #include <linux/platform_device.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> @@ -28,24 +28,30 @@ #include <linux/init.h> #include <linux/err.h> -#define MC13783_ADC_NAME "mc13783-adc" +#define DRIVER_NAME "mc13783-adc" + +/* platform device id driver data */ +#define MC13783_ADC_16CHANS 1 +#define MC13783_ADC_BPDIV2 2 struct mc13783_adc_priv { struct mc13xxx *mc13xxx; struct device *hwmon_dev; + char name[10]; }; static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "mc13783_adc\n"); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", priv->name); } static int mc13783_adc_read(struct device *dev, struct device_attribute *devattr, unsigned int *val) { - struct platform_device *pdev = to_platform_device(dev); - struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); unsigned int channel = attr->index; unsigned int sample[4]; @@ -68,16 +74,21 @@ static ssize_t mc13783_adc_read_bp(struct device *dev, struct device_attribute *devattr, char *buf) { unsigned val; + struct platform_device *pdev = to_platform_device(dev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; int ret = mc13783_adc_read(dev, devattr, &val); if (ret) return ret; - /* - * BP (channel 2) reports with offset 2.4V to the actual value to fit - * the input range of the ADC. unit = 2.25mV = 9/4 mV. - */ - val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; + if (driver_data & MC13783_ADC_BPDIV2) + val = DIV_ROUND_CLOSEST(val * 9, 2); + else + /* + * BP (channel 2) reports with offset 2.4V to the actual value + * to fit the input range of the ADC. unit = 2.25mV = 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; return sprintf(buf, "%u\n", val); } @@ -114,12 +125,21 @@ static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13); static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14); static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15); -static struct attribute *mc13783_attr[] = { +static struct attribute *mc13783_attr_base[] = { &dev_attr_name.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in7_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mc13783_group_base = { + .attrs = mc13783_attr_base, +}; + +/* these are only used if MC13783_ADC_16CHANS is provided in driver data */ +static struct attribute *mc13783_attr_16chans[] = { &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, &sensor_dev_attr_in10_input.dev_attr.attr, @@ -127,8 +147,8 @@ static struct attribute *mc13783_attr[] = { NULL }; -static const struct attribute_group mc13783_group = { - .attrs = mc13783_attr, +static const struct attribute_group mc13783_group_16chans = { + .attrs = mc13783_attr_16chans, }; /* last four channels may be occupied by the touchscreen */ @@ -156,24 +176,37 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) { struct mc13783_adc_priv *priv; int ret; + const struct platform_device_id *id = platform_get_device_id(pdev); + char *dash; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + snprintf(priv->name, ARRAY_SIZE(priv->name), "%s", id->name); + dash = strchr(priv->name, '-'); + if (dash) + *dash = '\0'; platform_set_drvdata(pdev, priv); /* Register sysfs hooks */ - ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group); + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_base); if (ret) - goto out_err_create1; + goto out_err_create_base; + + if (id->driver_data & MC13783_ADC_16CHANS) { + ret = sysfs_create_group(&pdev->dev.kobj, + &mc13783_group_16chans); + if (ret) + goto out_err_create_16chans; + } if (!mc13783_adc_use_touchscreen(pdev)) { ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); if (ret) - goto out_err_create2; + goto out_err_create_ts; } priv->hwmon_dev = hwmon_device_register(&pdev->dev); @@ -184,17 +217,20 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) goto out_err_register; } - return 0; out_err_register: if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); -out_err_create2: +out_err_create_ts: - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); -out_err_create1: + if (id->driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); +out_err_create_16chans: + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); +out_err_create_base: platform_set_drvdata(pdev, NULL); kfree(priv); @@ -205,13 +241,17 @@ out_err_create1: static int __devexit mc13783_adc_remove(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; hwmon_device_unregister(priv->hwmon_dev); if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); + if (driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); platform_set_drvdata(pdev, NULL); kfree(priv); @@ -219,12 +259,26 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id mc13783_adc_idtable[] = { + { + .name = "mc13783-adc", + .driver_data = MC13783_ADC_16CHANS, + }, { + .name = "mc13892-adc", + .driver_data = MC13783_ADC_BPDIV2, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable); + static struct platform_driver mc13783_adc_driver = { - .remove = __devexit_p(mc13783_adc_remove), + .remove = __devexit_p(mc13783_adc_remove), .driver = { .owner = THIS_MODULE, - .name = MC13783_ADC_NAME, + .name = DRIVER_NAME, }, + .id_table = mc13783_adc_idtable, }; static int __init mc13783_adc_init(void) @@ -243,4 +297,3 @@ module_exit(mc13783_adc_exit); MODULE_DESCRIPTION("MC13783 ADC driver"); MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" MC13783_ADC_NAME); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c new file mode 100644 index 000000000000..d0afc0cd3ff4 --- /dev/null +++ b/drivers/hwmon/mcp3021.c @@ -0,0 +1,171 @@ +/* + * mcp3021.c - driver for the Microchip MCP3021 chip + * + * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. + * Author: Mingkai Hu <Mingkai.hu@freescale.com> + * + * This driver export the value of analog input voltage to sysfs, the + * voltage unit is mV. Through the sysfs interface, lm-sensors tool + * can also display the input voltage. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/device.h> + +/* Vdd info */ +#define MCP3021_VDD_MAX 5500 +#define MCP3021_VDD_MIN 2700 +#define MCP3021_VDD_REF 3300 + +/* output format */ +#define MCP3021_SAR_SHIFT 2 +#define MCP3021_SAR_MASK 0x3ff + +#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */ +#define MCP3021_OUTPUT_SCALE 4 + +/* + * Client data (each client gets its own) + */ +struct mcp3021_data { + struct device *hwmon_dev; + u32 vdd; /* device power supply */ +}; + +static int mcp3021_read16(struct i2c_client *client) +{ + int ret; + u16 reg; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + /* The output code of the MCP3021 is transmitted with MSB first. */ + reg = be16_to_cpu(buf); + + /* + * The ten-bit output code is composed of the lower 4-bit of the + * first byte and the upper 6-bit of the second byte. + */ + reg = (reg >> MCP3021_SAR_SHIFT) & MCP3021_SAR_MASK; + + return reg; +} + +static inline u16 volts_from_reg(u16 vdd, u16 val) +{ + if (val == 0) + return 0; + + val = val * MCP3021_OUTPUT_SCALE - MCP3021_OUTPUT_SCALE / 2; + + return val * DIV_ROUND_CLOSEST(vdd, + (1 << MCP3021_OUTPUT_RES) * MCP3021_OUTPUT_SCALE); +} + +static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp3021_data *data = i2c_get_clientdata(client); + int reg, in_input; + + reg = mcp3021_read16(client); + if (reg < 0) + return reg; + + in_input = volts_from_reg(data->vdd, reg); + return sprintf(buf, "%d\n", in_input); +} + +static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); + +static int mcp3021_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct mcp3021_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + data = kzalloc(sizeof(struct mcp3021_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + + if (client->dev.platform_data) { + data->vdd = *(u32 *)client->dev.platform_data; + if (data->vdd > MCP3021_VDD_MAX || + data->vdd < MCP3021_VDD_MIN) { + err = -EINVAL; + goto exit_free; + } + } else + data->vdd = MCP3021_VDD_REF; + + err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); +exit_free: + kfree(data); + return err; +} + +static int mcp3021_remove(struct i2c_client *client) +{ + struct mcp3021_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); + kfree(data); + + return 0; +} + +static const struct i2c_device_id mcp3021_id[] = { + { "mcp3021", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp3021_id); + +static struct i2c_driver mcp3021_driver = { + .driver = { + .name = "mcp3021", + }, + .probe = mcp3021_probe, + .remove = mcp3021_remove, + .id_table = mcp3021_id, +}; + +module_i2c_driver(mcp3021_driver); + +MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>"); +MODULE_DESCRIPTION("Microchip MCP3021 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index deb12c982800..d887cb3b72e8 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -72,8 +72,10 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define TEMP_CRIT_HYST 2 #define TEMP_WARN 3 #define TEMP_WARN_HYST 4 -/* only crit and crit_hyst affect real-time alarm status - * current crit crit_hyst warn warn_hyst */ +/* + * only crit and crit_hyst affect real-time alarm status + * current crit crit_hyst warn warn_hyst + */ static const u16 W83795_REG_TEMP[][5] = { {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */ {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */ @@ -354,26 +356,34 @@ struct w83795_data { u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */ u8 temp_src[3]; /* Register value */ - u8 enable_dts; /* Enable PECI and SB-TSI, + u8 enable_dts; /* + * Enable PECI and SB-TSI, * bit 0: =1 enable, =0 disable, - * bit 1: =1 AMD SB-TSI, =0 Intel PECI */ + * bit 1: =1 AMD SB-TSI, =0 Intel PECI + */ u8 has_dts; /* Enable monitor DTS temp */ s8 dts[8]; /* Register value */ u8 dts_read_vrlsb[8]; /* Register value */ s8 dts_ext[4]; /* Register value */ - u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, + u8 has_pwm; /* + * 795g supports 8 pwm, 795adg only supports 2, * no config register, only affected by chip - * type */ - u8 pwm[8][5]; /* Register value, output, freq, start, - * non stop, stop time */ + * type + */ + u8 pwm[8][5]; /* + * Register value, output, freq, start, + * non stop, stop time + */ u16 clkin; /* CLKIN frequency in kHz */ u8 pwm_fcms[2]; /* Register value */ u8 pwm_tfmr[6]; /* Register value */ u8 pwm_fomc; /* Register value */ - u16 target_speed[8]; /* Register value, target speed for speed - * cruise */ + u16 target_speed[8]; /* + * Register value, target speed for speed + * cruise + */ u8 tol_speed; /* tolerance of target speed */ u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */ u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */ @@ -482,8 +492,10 @@ static void w83795_update_limits(struct i2c_client *client) /* Read the fan limits */ lsb = 0; /* Silent false gcc warning */ for (i = 0; i < ARRAY_SIZE(data->fan); i++) { - /* Each register contains LSB for 2 fans, but we want to - * read it only once to save time */ + /* + * Each register contains LSB for 2 fans, but we want to + * read it only once to save time + */ if ((i & 1) == 0 && (data->has_fan & (3 << i))) lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); @@ -665,9 +677,11 @@ static struct w83795_data *w83795_update_device(struct device *dev) w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT)); } - /* Update intrusion and alarms + /* + * Update intrusion and alarms * It is important to read intrusion first, because reading from - * register SMI STS6 clears the interrupt status temporarily. */ + * register SMI STS6 clears the interrupt status temporarily. + */ tmp = w83795_read(client, W83795_REG_ALARM_CTRL); /* Switch to interrupt status for intrusion if needed */ if (tmp & ALARM_CTRL_RTSACS) @@ -929,6 +943,14 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, if (val < 1 || val > 2) return -EINVAL; +#ifndef CONFIG_SENSORS_W83795_FANCTRL + if (val > 1) { + dev_warn(dev, "Automatic fan speed control support disabled\n"); + dev_warn(dev, "Build with CONFIG_SENSORS_W83795_FANCTRL=y if you want it\n"); + return -EOPNOTSUPP; + } +#endif + mutex_lock(&data->update_lock); switch (val) { case 1: @@ -1595,8 +1617,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define NOT_USED -1 -/* Don't change the attribute order, _max, _min and _beep are accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _max, _min and _beep are accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_IN(index) { \ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ IN_READ, index), \ @@ -1610,8 +1634,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, show_alarm_beep, store_beep, BEEP_ENABLE, \ index + ((index > 14) ? 1 : 0)) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_FAN(index) { \ SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ NULL, FAN_INPUT, index - 1), \ @@ -1625,23 +1651,25 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, #define SENSOR_ATTR_PWM(index) { \ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ store_pwm, PWM_OUTPUT, index - 1), \ + SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ + SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \ + show_pwm_mode, NULL, NOT_USED, index - 1), \ + SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_FREQ, index - 1), \ SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_START, index - 1), \ SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \ - SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ - show_pwm, store_pwm, PWM_FREQ, index - 1), \ - SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ - show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ - SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \ - show_pwm_mode, NULL, NOT_USED, index - 1), \ SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \ show_fanin, store_fanin, FANIN_TARGET, index - 1) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_DTS(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ show_dts_mode, NULL, NOT_USED, index - 7), \ @@ -1660,8 +1688,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) } -/* Don't change the attribute order, _beep is accessed by index - * somewhere else in the code */ +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ #define SENSOR_ATTR_TEMP(index) { \ SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ @@ -1867,8 +1897,10 @@ static int w83795_get_device_id(struct i2c_client *client) device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID); - /* Special case for rev. A chips; can't be checked first because later - revisions emulate this for compatibility */ + /* + * Special case for rev. A chips; can't be checked first because later + * revisions emulate this for compatibility + */ if (device_id < 0 || (device_id & 0xf0) != 0x50) { int alt_id; @@ -1920,8 +1952,10 @@ static int w83795_detect(struct i2c_client *client, return -ENODEV; } - /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR - should match */ + /* + * If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR + * should match + */ if ((bank & 0x07) == 0) { i2c_addr = i2c_smbus_read_byte_data(client, W83795_REG_I2C_ADDR); @@ -1933,10 +1967,12 @@ static int w83795_detect(struct i2c_client *client, } } - /* Check 795 chip type: 795G or 795ADG - Usually we don't write to chips during detection, but here we don't - quite have the choice; hopefully it's OK, we are about to return - success anyway */ + /* + * Check 795 chip type: 795G or 795ADG + * Usually we don't write to chips during detection, but here we don't + * quite have the choice; hopefully it's OK, we are about to return + * success anyway + */ if ((bank & 0x07) != 0) i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank & ~0x07); @@ -1953,6 +1989,14 @@ static int w83795_detect(struct i2c_client *client, return 0; } +#ifdef CONFIG_SENSORS_W83795_FANCTRL +#define NUM_PWM_ATTRIBUTES ARRAY_SIZE(w83795_pwm[0]) +#define NUM_TEMP_ATTRIBUTES ARRAY_SIZE(w83795_temp[0]) +#else +#define NUM_PWM_ATTRIBUTES 4 +#define NUM_TEMP_ATTRIBUTES 8 +#endif + static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, const struct device_attribute *)) { @@ -2006,24 +2050,18 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, } } -#ifdef CONFIG_SENSORS_W83795_FANCTRL for (i = 0; i < data->has_pwm; i++) { - for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) { + for (j = 0; j < NUM_PWM_ATTRIBUTES; j++) { err = fn(dev, &w83795_pwm[i][j].dev_attr); if (err) return err; } } -#endif for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { if (!(data->has_temp & (1 << i))) continue; -#ifdef CONFIG_SENSORS_W83795_FANCTRL - for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) { -#else - for (j = 0; j < 8; j++) { -#endif + for (j = 0; j < NUM_TEMP_ATTRIBUTES; j++) { if (j == 7 && !data->enable_beep) continue; err = fn(dev, &w83795_temp[i][j].dev_attr); @@ -2183,8 +2221,10 @@ static int w83795_probe(struct i2c_client *client, /* The W83795G has a dedicated BEEP pin */ data->enable_beep = 1; } else { - /* The W83795ADG has a shared pin for OVT# and BEEP, so you - * can't have both */ + /* + * The W83795ADG has a shared pin for OVT# and BEEP, so you + * can't have both + */ tmp = w83795_read(client, W83795_REG_OVT_CFG); if ((tmp & OVT_CFG_SEL) == 0) data->enable_beep = 1; |